前提とRoute model Bindingとは
ルーティングでid
などを指定して受け取るのを、Controllerに来た時点でモデルインスタンスを受け取れる
Route::get('{id}', function($id) {
Post::find($id);
});
// ↓↓↓↓↓↓
Route::get('{photo}', function(Photo $photo) {
});
Route::resource
やRoute::apiResource
というのがあり、これはCRUD系のルートを一括定義してくれる。
動詞 | URI | アクション | ルート名 |
---|---|---|---|
GET | /photos | index | photos.index |
GET | /photos/create | create | photos.create |
POST | /photos | store | photos.store |
GET | /photos/{photo} | show | photos.show |
GET | /photos/{photo}/edit | edit | photos.edit |
PUT/PATCH | /photos/{photo} | update | photos.update |
DELETE | /photos/{photo} | destroy | photos.destroy |
※上記はRoute::resourceの定義されるもの
これらは定義されたルートを見るとわかるように、コントローラー側で下記のように記述することで対象のモデルを受け取れる
ルートモデルバインディングは基本的にプライマリーキーで処理される
// PhotoのPKがidなのでidで検索され取得されたモデルが渡ってくる
public function show(Photo $photo)
{
}
idで検索されるがカスタマイズもできる
1
Route::get('/photos/{photo:slug}', function(Photo $photo) {
});
2
/**
* モデルのルートキーの取得
*
* @return string
*/
public function getRouteKeyName()
{
return 'slug';
}
3
public function boot()
{
Route::bind('photo', function ($value) {
return Photo::where('slug', $value)->firstOrFail();
});
}
しかし1番目はRoute::resource
、Route::apiResource
では使えない
2、3番目はRoute::resource
、Route::apiResource
でも基本的に問題ないが、全体が変更される。
常に指定したバインドされる際にキーが使われる。
例えば、ユーザー側と管理側と分ける場合、どちらも指定したキーでバインドすることを意識する必要がある。
やりたいこと
1番目のように、ルートごとに明示したキーを使用したい。
やりかた
scopedを使う。
本来下記のように、ネストしたリソースを定義し子リソースを取得するフィールド指定するときにscopedメソッドを使用します。
そもそもドキュメントにもそのような用途での記述でした
https://laravel.com/docs/9.x/controllers#restful-scoping-resource-routes
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos.comments', PhotoCommentController::class)->scoped([
'comment' => 'slug',
]);
しかしこれはネストしていなくても使用できます
use App\Http\Controllers\PhotoCommentController;
Route::resource('photos', PhotoCommentController::class)->scoped([
'photo' => 'slug',
]);
これでこのルートだけは、指定したキーを使用してモデルを取得できるようになりました。