Laravel 5.4 (4) ルーティングとビューのちょっとだけ入口

ルーティングを定義する前にビューを作っておきたいのは、もしかしたら順番としては逆なのかもしれませんが、「ルーティングしてもビューがなかったらエラーになるじゃないか」という、わりとどうでもいいこだわりから、ビューを作ってみます。

ビュー(View)

表示に関する処理をするところです。
WebアプリケーションではHTMLを出力するところ、と言い換えれば分かりやすいかもしれません。
で、Laravelのようなたくさんのディレクトリ階層を持つフレームワークでは、配置場所を探すのもイヤになりそうですが、リファレンスにはresources/viewsに配置と記載があります。

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>メインメニュー</title>
</head>
<body>
<h1>Main Menu</h1>
</body>
</html>

LaravelではBladeというテンプレートエンジンを使っています。
Bladeテンプレートエンジンについては、Laravelリファレンスにも記述がありますが、読んだだけではチンプンカンプンです。
とりあえずルーティングを確認してみたいだけなので、ただのHTMLで書かれたテンプレートを置きました。

ファイル名はmain.blade.phpと付与しました。
mainはビューを指定するときの名称。
bladeはLaravel標準テンプレートのbladeを使っていることを表しています。
拡張子はphpでお願いします。
結果、main.blade.php
そうしておけば、コントローラーでview('main');と指定すればこのビューが読み込まれます。

で、そのビューを表示させるだけのルーティングをroutes\web.phpに追加。

Route::get('/main', function () {
	return view('main');
});

クロージャーがそのまんまコントローラーとなっております。
/mainにブラウザでアクセスすると、ビューが表示されます。
Javaのようにコンテナの再起動もなにもしなくていいので、何かちょっと不思議な感じがします。

ビューの階層化

アプリケーションが複雑になると、ビューの配置にもディレクトリ階層を持たせたくなるのですが、その場合どうするのでしょうか。

resources/viewsの中を覗いてみると、以前作った認証のビューらしきものが、階層を持たせて配置されています。
resources\views\auth\login.blade.phpという風に。
このビューを表示させているであろうコントローラー(で使われているtrait AuthenticatesUsers)のでは、以下のように記述されています。

public function showLoginForm()
    {
        return view('auth.login');
    }

ドットで区切って階層を指定するようです。

ルーティング

要するに、特定のURLにアクセスがあったときに何をさせるかを決めることです。
Laravel 5.4のリファレンスによれば、様々なルーティング方法があるようですが、ざっと眺めたところ以下の事項を理解していれば大抵のことは実現できそうです。

基本的なルーティング

  • Route::get($uri, $callback);
  • Route::post($uri, $callback);
  • Route::put($uri, $callback); // あまり使わない
  • Route::patch($uri, $callback); //5.2で追加 使ったことがない 正直なところPATCHメソッドというものを初めて聞いた
  • Route::delete($uri, $callback); // あまり使わない
  • Route::options($uri, $callback); // 5.2で追加 あまり使わない
  • Route::match([‘get’, ‘post’], ‘/’, function () {
    //
    });
  • Route::any(‘foo’, function () {
    //
    });

getやpostはそれぞれのHTTPメソッドに対応しています。
それぞれのメソッドの意味はwikipediaか何かで確認するといいと思いますが、通常はgetとpostで事足りそうに思う。
matchとanyはHTTPメソッドではなく、複数のメソッドに対応したルートを設定するときに使うものです。

CSRF保護

webルートファイル中で定義され、POST、PUT、DELETEルートへ送信されるHTMLフォームはすべて、CSRFトークンフィールドを含んでいる必要があります。

ということらしいです。
この程度のセキュリティ機能が強制されているのはとてもいいと思います。
ただ、REST APIとしてPOSTで更新させたい場合などは何等かの回避策が必要そうですね。

ルートパラメーター

任意パラメータ

パラメータ付きルートを設定する。

ルーティングについては、それ以外のリファレンスは必要に応じて読めばいいと思います。ルートグループなんかは規模が少し大きくなったらすぐさま使いたくなりそうですけどね。(そして次回で早速使いました・・・)

コントローラー

先のルーティングの定義に戻りましょう。

Route::get('/main', function () {
	return view('main');
});

と記述すると、ここにはルーティングとビューの指定しかないようにも見えますが、先にも書いたようにこのビューの指定をしている箇所がコントローラーです。
コントローラーをクロージャーとして書いているのでなんとなく実感がないというか、分かりにくいですね。
コントローラーとして、クラスのメソッドを指定しているのが、これも既出の以下のような記述です。

Route::get('/home', 'HomeController@index');

このルーティングでは、getの第2引数でコントローラーとして実行するクラスとメソッドを指定しています。

おおざっぱには「表示やデータを直接操作しない、入力によってどのようなデータ操作をするか判断するところ」です。
と書くとこれまた議論が巻き起こるので、ViewでもModelでもないところ、としておきます。
私はMVC原理主義者ではないですが(経験上、結局複雑になってしまうMVCがいくつもありました。もちろんその中にはMVCの誤用も含まれていますが)、みんなの共通認識として処理を記述する場所が分かれているのはいいかな、程度に考えることにしています。

デフォルトの認証機能をインストールすると、すでにweb.phpへ/homeというルーティングが定義されています。

Route::get('/home', 'HomeController@index');

以下がそのHomeControllerクラス。

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('home');
    }
}

と、同時に認証の対象であるURLなためか、コンストラクタでmiddlewareが指定されています。

public function __construct()
    {
        $this->middleware('auth');
    }

ここで呼び出しているmiddlewareメソッドはどこにあるのか、ずいぶん探したところ、基底クラスであるabstractなControllerクラスに定義されています。

このControllerクラスにはその他にもcallActionというメソッドも定義されていますが、いつコールされるものか分からない。
Execute an action on the controller.と記述されているので、関数呼び出しのインターフェイスを抽象化しているのでしょう。
と思って探したら、Illuminate\Routing\ControllerDispatcherから呼び出されています。

/**
     * Dispatch a request to a given controller and method.
     *
     * @param  \Illuminate\Routing\Route  $route
     * @param  mixed  $controller
     * @param  string  $method
     * @return mixed
     */
    public function dispatch(Route $route, $controller, $method)
    {
        $parameters = $this->resolveClassMethodDependencies(
            $route->parametersWithoutNulls(), $controller, $method
        );

        if (method_exists($controller, 'callAction')) {
            return $controller->callAction($method, $parameters);
        }

        return $controller->{$method}(...array_values($parameters));
    }

処理を切り替えるのか、別の処理を実行したいときに呼ばれる様子。
dispatchメソッドはさらにIlluminate\Routing\Routeというおなじみのところから以下のように呼び出されています。

/**
     * Run the route action and return the response.
     *
     * @return mixed
     *
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
     */
    protected function runController()
    {
        return (new ControllerDispatcher($this->container))->dispatch(
            $this, $this->getController(), $this->getControllerMethod()
        );
    }

$this->getControllerMethod()でメソッドを取り出しているところを見ると、事前にどこかで設定されいてる模様。
Route::getの第二引数が$actionなので、ここで指定するのでしょう。

ということは、Route::getの第2引数で指定するクラスはControllerクラスを継承している必要があるのですね。
という当たり前の結論に到達しました。

実際にこのコントローラーのサブクラスを作ってみたいけれど、どこに作るのが正しいのか。
どこに作れば自動的に読み込まれるのか。
HomeController.phpクラスはapp\Http\Controllersに作られています。
そして、そこが自由にコントローラーを入れていいい場所のようです。
https://readouble.com/laravel/5.4/ja/controllers.html
このページにここまでで調べたことが全部書いてあって微妙な気持ちです。
でも、コントローラーはコントローラークラスを継承する必要があるって、まぁ、調べなくても分かりますよね。

調べなくてもいいことを深く調べて、ビューにはほとんど手を付けられませんでした。

■□■ Laravel入門 □■□
Laravel 5.4 (1)事始め
Laravel 5.4 (2)インストール
Laravel 5.4 (3)ルーティング基礎(認証)
Laravel 5.4 (4) ルーティングとビューのちょっとだけ入口