基本的なルーティング

アプリケーションのほとんどのルートは、app/Http/routes.phpファイルの中で定義することになります。このファイルは、App\Providers\RouteServiceProviderクラスによりロードされています。一番基本的なLaravelのルートは、URIと「クロージャー」を指定します。

Route::get('/', function () {
    return 'Hello World';
});

Route::post('foo/bar', function () {
    return 'Hello World';
});

Route::put('foo/bar', function () {
    //
});

Route::delete('foo/bar', function () {
    //
});

複数のHTTP動詞に対応するルートの登録

複数のHTTP動詞に対応するルートを登録する必要が時々起きます。Routeファサードmatchメソッドを使用してください。

Route::match(['get', 'post'], '/', function () {
    return 'Hello World';
});

もしくは全HTTP動詞に対応するanyメソッドを使い、ルート登録することもできます。

Route::any('foo', function () {
    return 'Hello World';
});

ルートに対応するURLの生成

アプリケーションのルートに対するURLを生成するには、urlヘルパーを使ってください。

$url = url('foo');

ルートパラメーター

必須パラメータ

もちろん、ルートの中のURIセグメントを取り出す必要が起きることもあります。たとえば、URLからユーザーIDを取り出したい場合です。ルートパラメーターを定義してください。

Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

ルートで必要なだけのルートパラメーターを定義することができます。

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

ルートパラメーターは常に波括弧で囲います。パラメーターはそのルートが実行される時に「クロージャー」に渡されます。

注意: ルートパラメーターは-文字を含むことができません。代わりに下線(_)を使ってください。

任意パラメータ

ルートパラメーターが必要だけれど、必須ではないパラメーターにしたい場合もあるでしょう。その場合はパラメーター名の後ろに、?マークを付けてください。

Route::get('user/{name?}', function ($name = null) {
    return $name;
});

Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

正規表現による限定

ルート定義でwhereを使用し、一致するルートパラメーターのフォーマットを限定することができます。whereメソッドはパラメーターの名前と、どのように限定するかを表す正規表現を引数に取ります。

Route::get('user/{name}', function ($name) {
    //
})
->where('name', '[A-Za-z]+');

Route::get('user/{id}', function ($id) {
    //
})
->where('id', '[0-9]+');

Route::get('user/{id}/{name}', function ($id, $name) {
    //
})
->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

グローバルな限定

ある名前のルートパラメーターをいつも正規表現で限定したい場合は、patternメソッドを使用してください。RouteServiceProviderbootメソッドでこうしたパターンを定義してください。

/**
 * ルートモデルの結合、パターンフィルタなどの定義
 *
 * @param  \Illuminate\Routing\Router  $router
 * @return void
 */
public function boot(Router $router)
{
    $router->pattern('id', '[0-9]+');

    parent::boot($router);
}

パターンを一度定義すれば、そのパラメーター名を使用している全ルートで自動的に適用されます。

Route::get('user/{id}', function ($id) {
    // {id}が数値の場合のみ呼び出される
});

名前付きルート

名前付きルートは特定のルートへのURLを生成したり、リダイレクトしたりする場合に便利です。ルートを定義するときに、as配列キーを使用して名前を指定してください。

Route::get('user/profile', ['as' => 'profile', function () {
    //
}]);

コントローラーアクションに対しても名前を付けることができます。

Route::get('user/profile', [
    'as' => 'profile', 'uses' => 'UserController@showProfile'
]);

ルート定義の配列の中でルート名を指定する代わりに、ルート定義の最後にnameメソッドをつなげることもできます。

Route::get('user/profile', 'UserController@showProfile')->name('profile');

ルートグループと名前付きルート

ルートグループを使用している場合、ルートグループの属性配列でasキーワードを指定し、グループ内の全ルートに共通な名前のプリフィックスを指定することができます。

Route::group(['as' => 'admin::'], function () {
    Route::get('dashboard', ['as' => 'dashboard', function () {
        // ルート名は"admin::dashboard"
    }]);
});

名前付きルートへのURLを生成する

ルートに一度名前を付ければ、その名前をroute関数で使用することで、URLを生成したり、リダイレクトしたりできます。

$url = route('profile');

$redirect = redirect()->route('profile');

そのルートでパラメーターを定義してある場合は、routeメソッドの第2引数としてパラメーターを渡してください。指定されたパラメーターは自動的にURLへ埋め込まれます。

Route::get('user/{id}/profile', ['as' => 'profile', function ($id) {
    //
}]);

$url = route('profile', ['id' => 1]);

ルートグループ

ルートグループは多くのルートで共通なミドルウェアや名前空間のようなルート属性をルートごとに定義するのではなく、一括して適用するための手法です。Route::groupメソッドの最初の引数には、共通の属性を配列で指定します。

ルートグループについてより理解を深めるために、この機能の一般的なユースケースを学んでください。

ミドルウェア

グループの中の全ルートにミドルウェアを指定するには、middlewareキーをグループ属性配列に使用します。この配列で指定した順番にミドルウェアは実行されます。

Route::group(['middleware' => 'auth'], function () {
    Route::get('/', function ()    {
        // authミドルウェアが使用される
    });

    Route::get('user/profile', function () {
        // authミドルウェアが使用される
    });
});

名前空間

ルートグループのもう一つのよくあるユースケースは、コントローラーのグループ内に同じPHP名前空間を指定する場合です。グループ中の全コントローラーに対して適用する名前空間をグループ属性配列のnamespaceキーを使用し指定します。

Route::group(['namespace' => 'Admin'], function()
{
    // "App\Http\Controllers\Admin"名前空間下のコントローラー

    Route::group(['namespace' => 'User'], function()
    {
        // "App\Http\Controllers\Admin\User"名前空間下のコントローラー
    });
});

App\Http\Controllers名前空間をコントローラールート登録時に毎回指定しなくても済むように、RouteServiceProviderが名前空間グループの中でroutes.phpファイルを読み込み、デフォルト指定していることを覚えておいてください。これにより、先頭のApp\Http\Controllers名前空間を省略でき、続きの部分を指定するだけで済みます。

サブドメインルーティング

ルートグループはワイルドカードサブドメインをルート定義するためにも使えます。サブドメインの部分を取り出しルートやコントローラーで使用するために、ルートURIにおけるルートパラメーターのように指定できます。サブドメインはグループ属性配列でdomainキーにより指定します。

Route::group(['domain' => '{account}.myapp.com'], function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

ルートプレフィックス

prefixグループ配列属性はグループ内の各ルートに対して、指定されたURIのプレフィックスを指定するために使用します。たとえばグループ内の全ルートのURIにadminを付けたければ、次のように指定します。

Route::group(['prefix' => 'admin'], function () {
    Route::get('users', function ()    {
        // "/admin/users" URLに一致
    });
});

prefixパラメーターは、グループ内のルートに対して、共通のパラメーターを指定するためにも使用できます。

Route::group(['prefix' => 'accounts/{account_id}'], function () {
    Route::get('detail', function ($account_id)    {
        // "accounts/{account_id}/detail" URLに一致
    });
});

CSRF保護

イントロダクション

Laravelでは、クロス・サイト・リクエスト・フォージェリからアプリケーションを簡単に守れます。クロス・サイト・リクエスト・フォージェリは悪意のあるエクスプロイトの一種であり、信頼できるユーザーになり代わり、認められていないコマンドを実行します。

Laravelは、アプリケーションにより管理されているアクティブなユーザーの各セッションごとに、CSRF「トークン」を自動的に生成しています。このトークンは認証済みのユーザーが、実装にアプリケーションに対してリクエストを送信しているのかを確認するために利用します。CSRFトークンを含む_token隠し入力フィールドを生成するには、csrf_fieldヘルパー関数が使用できます。

<?php echo csrf_field(); ?>

csrf_fieldヘルパーは、以下のHTMLを生成します。

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

もちろん、Bladeテンプレートエンジンを使用できます。

{{ csrf_field() }}

POST、PUT、DELETEリクエストのCSRFトークンを自分で確認する必要はありません。VerifyCsrfToken HTTPミドルウェアが、リクエスト中のトークンとセッションに保存されているトークンが一致するか、確認しています。

CSRF保護から特定URIを除外

一連のURIをCSRF保護より除外したい場合もあります。たとえば、Stripeを料金の受け取りに採用しており、そのWebフックシステムを利用している時は、LaravelのCSRF保護よりWebフック処理ルートを除外する必要があるでしょう。

VerifyCsrfTokenミドルウェアの$exceptプロパティに除外URIを追加してください。

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * CSRFの確認より除外するべきURI
     *
     * @var array
     */
    protected $except = [
        'stripe/*',
    ];
}

X-CSRF-TOKEN

更に追加でPOSTパラメーターとしてCSRFトークンを確認したい場合は、LaravelのVerifyCsrfTokenミドルウェアがX-CSRF-TOKENリクエストヘッダーもチェックします。たとえば、"meta"タグにトークンを保存します。

<meta name="csrf-token" content="{{ csrf_token() }}">

metaタグを作成したら、jQueryのようなライブラリーで、全リクエストヘッダーにトークンを追加できます。この手法によりAJAXベースのアプリケーションにシンプルで便利なCSRF保護を提供できます。

$.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
});

X-XSRF-TOKEN

Laravelは更にCSRFトークンをXSRF-TOKENクッキーの中に保存します。このクッキーの値をX-XSRF-TOKENリクエストヘッダーにセットすることが可能です。AngularのようなJavaScriptフレームワークでは、自動的に行われます。自動で行われない場合は、手動で値を設定する必要があります。

モデル結合ルート

Laravelルートモデル結合はルートクラスへインスタンスを注入する便利な方法を提供します。たとえばユーザーのIDを受け取る代わりに、その指定されたIDと一致するUserクラスインスタンス自体を注入できます。

最初にルーターのmodelメソッドで指定されたパラメーターに対するクラスを指定します。RouteServiceProvider::bootメソッドでモデル結合を定義してください。

引数をモデルに結合する

public function boot(Router $router)
{
    parent::boot($router);

    $router->model('user', 'App\User');
}

次に{user}パラメーターを含むルートを定義します。

$router->get('profile/{user}', function(App\User $user) {
    //
});

{user}パラメーターをApp\Userモデルへ結合しているため、Userインスタンスはルートへ注入されます。ですからたとえば、profile/1のリクエストでは、IDが1のインスタンスが注入されます。

注意: データベースに一致するモデルインスタンスが見つからない場合、自動的に404例外が投げられます。

独自の「見つかりません」動作を指定したい場合は、modelメソッドの第3引数としてクロージャーを渡してください。

$router->model('user', 'App\User', function() {
    throw new NotFoundHttpException;
});

独自の依存解決ロジックを使いたい場合は、Route::bindメソッドを利用します。bindメソッドに渡されたクロージャーはURIセグメントの値を受けとりますので、ルートへ注入したいクラスのインスタンスを返してください。

$router->bind('user', function($value) {
    return App\User::where('name', $value)->first();
});

見せかけのフォームメソッド

HTLMフォームはPUTPATCHDELETEアクションをサポートしていません。ですから、HTMLフォームから呼ばれるPUTPATCHDELETEルートを定義する時、フォームに_method隠しフィールドを追加する必要があります。_methodフィールドとして送られた値は、HTTPリクエストメソッドとして使用されます。

<form action="/foo/bar" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>

_method隠し入力フィールドを生成するために、method_fieldヘルパー関数を使用することもできます。

<?php echo method_field('PUT'); ?>

もちろん、Bladeテンプレートエンジンを使って書けます。

{{ method_field('PUT') }}

404エラーのthrow

手動で404エラーをルートから発生させるには2つの手法があります。最初の方法はabortヘルパーを使用する方法です。abortヘルパーは、Symfony\Component\HttpFoundation\Exception\HttpException例外を指定したステータスコードと共に投げる(throw)だけです。

abort(404);

2つめの方法はSymfony\Component\HttpKernel\Exception\NotFoundHttpExceptionのインスタンスを投げる方法です。

404例外の取り扱いやエラーのカスタム処理を行う詳しい方法は、ドキュメントのエラーの章をご覧ください。