Laravel は PHP フレームワークであり多くの場合、 Web サイト、Web アプリケーションの作成で使われます。Laravel にはルートを定義する仕組みがあります。
ルーティング 6.x Laravel
次の様にルート定義を書くと期待通りの動作をしません。
Route::group(static function () { Route::get('{memberId}', 'MemberController@show'); Route::get('download', 'MemberController@download'); });
/member/download という URL へのリクエストは MemberController@show で処理されてしまいます。これは download という文字列がパラメータである {memberId} の値である、と処理されてしまうからです。show メソッドを会員の詳細を返すメソッドと仮定すると /member/download の結果は”会員が見つかりませんでした。”というエラーが返ってくる結果になるでしょう。
この問題の簡易的な対処方法は次です。
Route::group(static function () { Route::get('download', 'MemberController@download');// 下にあった download を上に移動 Route::get('{memberId}', 'MemberController@show'); });
Laravel のルーティングは URL に合致するルート定義を見つけた場合、そのルート定義にしたがって処理を実行する、という実装です。このためパラメータを要求するルートよりも先に、パラメータとして読める部分のあるルートを定義することで期待通りの URL と処理の紐づけができます。
この方法はあくまで簡易的な対処方法であり、行うべきルーティングの規模や複雑さが増すにつれてバグを生みやすくなります。この対処方法では定義されたルートの大部分を把握する必要があり、それが難しくなるためです。prefix メソッドや group メソッドでルートのグループと URL をまとめることで事故は起きにくくなりますが限度はあります。
各ルート定義の全容が把握できない規模になった時、並び替えによるルート優先度の変更による対処は実質不可能、あるいは非常にコストのかかる方法になります。このため並び順に関わらず望むルートが選ばれるルート定義方法に需要が生まれます。これは次の様なコードで実現できます。
Route::where([ 'memberId' => '\d+', // パラメータである memberId には数値のみを許す // 'memberId' => '(?!download$)\d+',// パラメータである memberId には特別な文字列である download を許さない // 'memberId' => '(?!download|download-csv)\d+',// 特別文字列が複数あるならパイプで区切る ])->group(static function () { Route::get('{memberId}', fn () => 'call memberId');// ↑で定めた正規表現に合致する文字列のみが memberId に入る Route::get('download', fn () => 'call download'); });
これは Laravel の正規表現制約機能を用いてパラメータとしてバインド可能な文字列を制限する方法です。
ルーティング 6.x Laravel#正規表現制約
ドキュメントの例にはありませんが where メソッドの後に group メソッドが使えて、グループの中限定で正規表現制約を使えます。グループを用いるのは正規表現制約をグループ外のルート定義に適用したくないためです。パラメータ名被りや処理で表現するものによって異なる制約が必要になることはままあります。
恐らく最も頻繁に使うの数値のみを許す正規表現を用いるパターンでしょう。これを使うだけでも他のルーティングを邪魔することは滅多にありません。もしパラメータの正規表現に合致してしまう特別な URL が必要な場合は(?!特別なURL)
を追加で用います。他のルーティングを知る必要がありますがピンポイントでパラメータ化の振る舞いを決められます。
この様にすると並び順に関わらずルーティングを期待通りに動作させやすいです。