ルーティング

この記事は自分の勉強のために書いています。
ソースはLaravel5.7の公式ドキュメントです。
新しいことは何もないので通常はそちらを参照してください。


基本的なルーティング

routes.phpの書き方

HTTP動詞に対応させたroutes.phpの書き方。RESTfulAPIを書く場合は必要なんだと思う。
anyを使うと全ての動詞に対応する。matchを使うと対応する動詞を選べる。

No 動詞 文法
1 GET Route::get($url,$callback);
2 POST Route::post($url,$callback);
3 PUT Route::put($url,$callback);
4 PATCH Route::patch($url,$callback);
5 DELETE Route::delete($url,$callback);
6 OPTIONS Route::options($url,$callback);
7 全て Route::any($url,$callback);
8 選んだ動詞 Route::match($array_of_verb,$url,$callback);

リダイレクトルート

単純にリダイレクトをかける場合は、わざわざコントローラを通さなくて良い。
(コントローラを通してた…)。
通常302で、301もルーティングだけで対応可能。

No HTTP Status Code 文法
1 302 Route::redirect(‘here’,’there’);
or Route::redirect(‘here’,’there’,302);
2 302 Route::redirect(‘here’,’there’,301);
or Route::permanentRedirect(‘hear’,’there’);

Viewルート

ルートから直接ビューを返す場合はコントローラを介さなくてもよい。

No 文法
1 Route::view($url,$view_name);
2 Route::view($url,$view_name,$array_of_parameters);
ex, Route::view($url,$view,[‘param1’=>100,’param2’=>200]);

CSRF保護

webルートに定義したPOST,PUT,DELETEへのルートへ送信するフォームは
一緒にCSRFトークンを送る必要がある(CSRFは別にまとめる予定)。

ルートパラメータ

URLの動作に必要なパラメタをクエリ文字列ではなくURLに含める書き方。
必須パラメータとしておけば省略は不可となり存在チェックを省略できる。

必須パラメータ

Curly bracketsでパラメタ名を囲んで定義する。
受けるクロージャはそのパラメタ名をもつ引数を使用する。


Route::get('application/{application_id}', function($application_id) {
   return $application_id;
});

任意パラメータ

Curly brackets内のパラメタ名を”?”で終わらすと任意パラメータになる。
受けるクロージャでは必ずデフォルト引数を設定する。


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

正規表現制約

ルートパラメタのフォーマットを正規表現で制約できる(そんなことできるんだ…)。
書き方は以下の通り。URLとクロージャは触らない。


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

グローバル制約

RouteServiceProviderのbootメソッドに全てのルートパラメタを制約するルールを書ける。
例えば以下のように書いておくと、全てのルートの”id”という名前のクエリパラメタが数値に制約される。


/**
 *
*/
public function boot()
{
   Route::pattern('id','[0-9]+');
   parent::boot();
}
/**
 *  routes.php
*/
Route::get('user/{id}',function($id) {
   return $id; // $idは数値に制約
});

名前付きルート

定義したルートに名前を付けておくことで、そのルートのURLを名前から引くことができる。

名前付きルート

例えばuser/{id}に対するルートにmypageという名前を付けるには以下。


Route::get('user/{id}',function($id) {
   return $id;
})->name('mypage');

名前付きルートへのURL取得

このルートへのURLはroute()により取得できる。
ルートが必須/任意パラメータを必要とする場合は第2引数で値を与える。


redirect()->route('mypage',['id'=>100]);

ルートグループ

複数のルートで共通の属性(=ミドルウェアと名前空間)をまとめて書ける。
グループはネストすることができる。ミドルウェアと名前空間は別途記載。

ミドルウェアによるグループ化

複数のルートで共通のミドルウェアを外出しする書き方は以下。
(もちろん個別に書くこともできる)


Route::middleware(['first','second'])->group(function() {
   Route::get('/',function(){
     // firstミドルウェア、secondミドルウェアを順番に使用
   });
   Route::get('/hoge/{id}',function($id){
     // firstミドルウェア、secondミドルウェアを順番に使用
   });
});

名前空間によるグループ化

複数のルートで同じPHP名前空間を使用する書き方は以下。
こうすることで、各コントローラのファイルの先頭に名前空間を書かなくてもよくなる。


Route::namespace('Namespace1')->group(function() {
   // App\Http\Controllers\Namespace1 名前空間下のコントローラ
   });
})/

<?php
   namespace App/Http/Controllers/Namespace1;  // <- これが不要になる

サブドメインによるグループ化

複数のルートが同じサブドメイン配下にあるような配置にする書き方は以下。(こんなことできるのか!)。


Route::domain('{subdomainname}.myapp.test')->group(function() {
   Route::get('user/{id}',function($id) {
      // subdomainname.myapp.test/user/100
   });
});

ルートプレフィックスによるグループ化

複数のルートが同じ文字列からスタートするように配置する書き方は以下。


Route::prefix('sameprefix')->group(function() {
   Route::get('user/{id}',function($id) {
      // /sameprefix/user/100
   });
   Route::get('group/{id}',function($id) {
      // /sameprefix/group/200
   });
});

ルート名プレフィックスによるグループ化

複数のルートのルート名が同じ文字列からスタートするように配置する書き方は以下。


Route::name('sameprefix')->group(function() {
   Route::get('user/{id}',function($id) {
      // 'sameprefix.user' というルート名
   })->name('user');
   Route::get('group/{id}',function($id) {
      // 'sameprefix.group' というルート名
   });
});

モデル結合ルート

ルートで与えた情報(多くの場合でモデルID)をコントローラに渡して
コントローラの中でモデルインスタンスを引くという使い方がほとんどなので、
ルートに含めたモデルIDからモデルインスタンスを解決してコントローラでそれを使うことができる。
当然、クロージャを直接書いても同じ。

暗黙のモデル結合ルート

どうやってルートパラメータとモデルインスタンスを紐づけるかというと、
なんと、PHPのタイプヒントを使って紐づける。

以下、$userがApp\Userクラスであるとタイプヒントされている。
ルートパラメータにuserがあるから、クロージャの中ではApp\Userのインスタンスを
$userを使って引いたオブジェクトを使用できる。
わざわざ$userを使ってApp\User::find($user)と書いたりエラーケースを書くのを省略できる。
App\User::find($user)が見つからなかったらルートは404を返す。すごいー。


Route::get('users/{user}',function(App\User $user) {
  // App\User $user を使用できる。
});

キー名がカスタマイズされたモデル結合ルート

モデルの主キーがidではないときimplicitにidからModelを引くことが出来ない。
モデル側でgetRouteKeyName()をオーバーライドして主キーを返せば良い。
ルートパラメータを新しい主キーであるものとしてインスタンスを探してくれる。


Route::get('users/{user}',function(App\User $user) {
  // App\User.userid が $user であるインスタンスを使用できる。
});

/**
 *  モデルの中
 *
*/
public function getRouteKeyName() {
   return 'userid';
}

明示的なモデル結合ルート

ルート名とタイプヒントを使ってimplicitにモデル結合する方法とは別に、
RouteServiceProviderクラスのboot()メソッドの中でexplicitにモデル結合を定義できる。
タイプヒントがなくても良いというのならexplicitに書く意味もあると思うのだけど、
タイプヒントが必要なのであれば、implicitな記法に対して冗長な気がするんだけども...。


/**
 * RouteServiceProvider
 *
*/
public function boot() {
   parent::boot();
   Route::model('user',App\User::class);
}

/**
 * routes.php
 *
*/
Route::get('user/{user}',function(App\User $user) {
});

依存解決ロジックのカスタマイズ

「ルートパラメータをモデルID、タイプヒントをモデルクラスとして、モデルIDを主キー(または別のキー)
を使って検索する」という単純なロジックとは異なるロジックを定義する方法が用意されている。
例えばモデルを主キーとは関係ないフィールドで検索したいときは以下みたいに書く。


/**
 * RouteServiceProvider
 *
*/
public function boot() {
   parent::boot();
   Route::bind('user', function($value) {
      return App\User::where('name',$value)->first() ?? abort(404);
   });
}

モデル側でresoluveRoutingBind()をオーバーライドすることでも実現可能。


/**
 * モデル
 *
*/
public function resolveRoutingBind($value) {
   return $this->where('name',$value)->first() ?? abort(404);
}

その他

フォールバックルート

フォールバックルートを書いておくと、routes.phpに書いたどのルートにも当たらなかったときに使われる。
通常404になるところを受ける。書く場合は最後に配置しないといけない。


Route::fallback(function(){
  // do something when 404.
});