nanisore oishisou

プログラマ、ララ・ベル子さん改めArm4さんのゆるふわ奮闘記。

ControllerとRouteを階層化

三度の飯よりコーディングっemoji
どうも、あなたのベル子です。

ブログがちょっとふざけすぎている技術的情報量が少ないので、そのうち入ったばかりの会社で「ブログふざけすぎの刑」を言い渡されないかと、ビクビクする日々を送っていたベル子ですが、
「いいぞ、もっとやれ」ということだったので、引き続きのびのびと新人プログラマライフをお届けしていこうと思います。

今日はとびきり長いので、時間のない人はまとめだけ読むことをオススメいたします。

突然ですが私の告白を聞いてください。
最近、もやっとしていることがあります。
言おうかどうしようか迷っちゃうんですけどぉ、やっぱり言っちゃおっかなぁ、でもどうしようかなぁ、だって誤解されるとやだしぃ、てゆーか大したことじゃないんだけどぉ、でもやっぱ告っちゃおっかなぁ。

ということで、

Laravel5系の情報と4系の情報をごちゃ混ぜに読んだり見たりしているので
やや頭がこんがらがってるのもあると思うんですが、
namespaceとautoload..........
この2つの概念がよく分かっていません!!!


先日、Controllerを階層化するという作業をしていて、サブフォルダにControllerを入れたら読み込んでくれなくなりました。
え、こういうこと?と思って以下のように書いてみました。

Route::controller('/top/second', 'second/SecondController');

ブブーー不正解でした。

よく分かんないけど、こういうことかな?

Route::controller('/top/second', 'second.SecondController');

ブブーー不正解でした。

やっぱ勘じゃ無理でした。うふふ。

そこで調べてみるとcomposer dump-autoloadをコマンドーんすると読み込まれるようになるっぽいので、そうしてみました。
確かにそれでコードの修正をせずともControllerは読み込まれるようにはなりました。
自分でやっておいてなんですが、摩訶不思議です。

しかし、今度はルーティングを階層化しようとすると、Classがないよって怒られました。
そこでサブフォルダをやっぱりなくしてルーティングの解決を先にやることにしたんです。

Route::controller('/top', 'TopController');
Route::controller('/top/second', 'SecondController');
Route::controller('/top/third', 'ThirdController');

こんな感じで。
が、相変わらずlaravelちゃんのご機嫌はナナメです。
そこで、以下のようにgroup化してみたんですが、それでもダメでした。
まったくいけずなんだからっ!

Route::group(['prefix' => 'top'], function() {
    Route::controller('/', 'TopController');
    Route::controller('/second', 'SecondController');
    Route::controller('/third', 'ThirdController');
});

でもphp artisan routeしてみると、確かにルートは作られているんですよ!
stack overflowで調べてもnamespaceを使いなさい(神様より)的な回答が多くて、namespaceを使う方法も試してみたのですが、いかんせんnamespaceを使う正しい作法がよく分かってないのでうまくいきません。

namespace.....使いたくないな.....だってよく分からないし。

そんな気持ちで別の方法を必死で探していると
「それルートのやつを一番下に持っていったら動くよ!」というアンビリーバブルなほど無邪気な書き込みを見つけました。
「まさかぁ〜、そんなわけぇ〜」と思いつつやってみたところ、なんということでしょう!
動きます!

なんでもその方の言うところによると、
honyahonya.com/top/secondとリクエストしても上から検索してしまって、ルートのTopControllerを最初に読みに行ってしまうから、とのことでした。
なるほど、確かにそれなら納得です。

Route::group(['prefix' => 'top'], function() {
    Route::controller('/second', 'SecondController');
    Route::controller('/third', 'ThirdController');
    Route::controller('/', 'TopController');
});

こんなふうに書いたら動きました。
そこで疲れ果ててしまったので、Controllerの階層化は「しなくてもよくない?しなくても分かりやすいと思います!」的なアピールをそれとなくマスターPGの方(ご主人様)にして帰ることにしました。


ーーー次の日

主「app/start/global.phpのClassLoader::addDirectoriesにサブフォルダを追記したら読み込めました。(だから、あとはお前が階層化しとけよな!)」


ガチョーーーン。
app/start/global.phpのClassLoader::addDirectoriesという存在自体を初めて知ったので、言われるがまま設定しました。ちゃんと動いたし階層化もできました。
ご主人様、私、言われたとおりに出来ました!
めでたしめでたし。

.......でも、なんか、まだもやっとします。
右から左でいいわけないじゃないか。
そもそもClassLoaderってなによ。
それよりも何よりも、私の手下感はんぱないじゃないのっ!!
(手下に違いないんですけど)
くぅ〜〜〜〜 orz

悔しいので、もう一度おうちで確認してみました。

ClassLoader::addDirectoriesのところに書いてあるコメントには、「ここに書けばcomposer updateしなくてもglobalのnamespaceにcontrollerをloadできるよ!」って書いてあります。

vender/composer/autoload_classmap.phpというファイルにオートロードされるClassの配列が書いてあるようなので、こちらを確認しながらdump-autoloadをしてみると、なんとなく謎が解けてきました。

dump-autoloadするとcomposer.jsonの以下の部分で指定したフォルダ内のClassをautoload_classmap.phpで配列を作ってmapしてくれるということのようです。

        "autoload": {
                "classmap": [
                        "app/commands",
                        "app/controllers",
                        "app/models",
                        "app/database/migrations",
                        "app/database/seeds",
                        "app/tests/TestCase.php"
                ]
        },

ですから、ClassLoader::addDirectoriesにフォルダを追加するのとcomposer dump-autoloadするのは方法が違うだけで、同じことをしてたということですね。

ということはControllerを階層化する場合は、フォルダを作ってcomposer dump-autoloadをするか、ClassLoader::addDirectoriesにフォルダを追記するかの2とおりの方法があって、どちらでもいい、というのが結論です。

もちろん上記のclassmapディレクトリの配下にないClassはロードしてくれないので、app配下に新規フォルダを作りたい場合はclassmapに追記しましょう。
不安な人はvender/composer/autoload_classmap.phpを確認してみることをオススメします。

URLの階層化と、同時進行していたので頭がこんがらがっていました。
routesでエラーが出ていたのとは別の話だったようです。


次にもやっとしてるルーティングですが、前についているスラッシュのせいかも、と思ってスラッシュを全部はずしてみましたが、やはりダメでした。
Route::controllerでルートを階層化する場合は下のサブフォルダから書いていかないとダメなようですね。

これで何とか、よく分からないnamespaceを使わずに済みました。えへへ。

えぇ?よく分からないままで終わらせていいのかですって?
それは、今がんばって勉強してるから近日公開してあげます(上からベル子)。
首を長くして待っててくだされ。


★Autoloadのまとめ
・サブフォルダを作ったときはcomposer dump-autoloadをするか、ClassLoader::addDirectoriesにフォルダを追記するかの2とおりの方法があって、そのどちらでもいい。
・Autoloadとはつまり、require_onceとかしなくても自動的に最初に必要なClassらを読み込んでくれる機能。←便利!

★Routeの階層化のまとめ
・Route::controllerでルートを階層化する場合は下のサブフォルダから書いていかないとダメ。
・Routeの書き方は大きく分けると3つある。下のまとめが分かりやすい。
http://qiita.com/michiomochi@github/items/de19c560bc1dc19d698c

★namespaceのまとめ
・今、勉強中


次回、「見知らぬnamespace」。この次もサービスサービスぅ♪てーでん。
私はもちろんアスカ推しですよーー。