Route model Binding とは
例を上げて説明します。
URIが/articles/ {id}
のようになってた時
普通ならばcontroller側でshow($id)
のようにして{id}
の部分を受け取るところを
/articles/ {article}
とすることで、controllerに来た時点で
Articleモデルのインスタンスとして受け取ることが出来るという機能です
公式: https://laravel.com/docs/5.6/routing#route-model-binding
参考(日本語): https://laravel10.wordpress.com/2015/03/28/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AElaravel-5-32-route-model-binding/
resource()に対応する
ルーティングがget()
やpatch()
で記述されていた場合
Route model Bindingに対応させると下記のようになります
Route::get('articles/{article}', 'ArticleController@show');
Route::patch('articles/{article}', 'ArticleController@update');
では、resource()
をルーティングの定義に使っていた場合
Route model Bindingに対応させようとすると、どうすればいいかを順番に書いていきます
(1) bindされる部分の名前を把握する
get()
やpatch()
の場合は、
上記の参考コードに書いたように
bindされる部分の名前は記述しているので明らかですが
resource()
記述だと何かわかりません。
まずはそこから調べます
アプリケーションdirの配下でphp artisan route:list
を実行してルーティング一覧を出してください
すると、resourceで記述されていた部分も展開され細かいURIが出力されます
| | POST | articles | articles.store | App\Http\Controllers\ArticlesController@store | web |
| | GET|HEAD | articles | articles.index | App\Http\Controllers\ArticlesController@index | web |
| | GET|HEAD | articles/create | articles.create | App\Http\Controllers\ArticlesController@create | web |
| | GET|HEAD | articles/{article} | articles.show | App\Http\Controllers\ArticlesController@show | web |
| | DELETE | articles/{article} | articles.destroy | App\Http\Controllers\ArticlesController@destroy | web |
| | PUT|PATCH | articles/{article} | articles.update | App\Http\Controllers\ArticlesController@update | web |
| | GET|HEAD | articles/{article}/edit | articles.edit | App\Http\Controllers\ArticlesController@edit | web |
すると、bindされる部分の名前が
個々の例において{article}
であることがわかります
(2)名前に対してbindするModelを定義する
この定義は、行う必要がある場合とそうでない場合があります
この対応が必要でない場合とは、
bindされる部分(ex: {article}
) の名前のModelが存在していて、かつ、bindしたいModelの名前と一致している時です。
ここの例であれば、Articleモデルをbindしたいという要件であれば特にbindの定義は必要ありません
では定義が必要な場合とは
bindされる部分(ex: {hoge}
) の名前のModelが存在しない、またはbindしたいModelの名前と一致していないときです
こういったことは、URIの構造上起きることはあると思います。
そんなときは、名前に対してbindするModelを定義します
Providers/RouteServiceProvider.php
に下記のように追記をします
class RouteServiceProvider extends ServiceProvider
{
public function boot()
{
//
parent::boot();
Route::model('hoge', Article::class); // <-追記
}
}
第一引数にbindされる部分(ex: {hoge}
) の名前
第二引数にbindするModel
を記述してください
(3)Controller側でモデルのインスタンスを受け取る
class ArticleController extends Controller
{
public function show(Article $article) // <- 引数にクラスの型宣言をする
{
dd($article);
//
}
これでRoute model bindingは完了です
bindされる部分の名前を変更したい時どうすればいいか
基本的にbindされる部分の名前は
resouce()の第一引数のnameの部分の単数形が割り当てられます。(不可算名詞ならそのまま)
URIに強いこだわりがなければURIの構造や綺麗さの観点で
nameの部分を変えるのが良いと私は思います
それ以外での方法で。。。となるとresourceの処理を追う限り難しいように見えます
なので、bindされる部分の名前を変更したい場合は、
resouce()の第一引数を変更する以外はないかと思われます(知見のある方いたら教えてほしい)
コメントいただいた方法でbind名の変更が可能だったので紹介します
mpywさん、ありがとうございます!!!!
bind名を上書き変更する
Route::resource('hoge', 'FugeController')
という定義があった時、bindされる部分の名前は{hoge}
になっています
これを{hoge}
から{article}
に変更します
パラメータのマッピングを上書きます
class RouteServiceProvider extends ServiceProvider
{
public function boot()
{
parent::boot();
ResourceRegistrar::setParameters(['hoge' => 'article']); // 第一引数に変更対象のbind名、第二引数に変更後のbind名
}
}
そしてrouteの方で
上書きしたマッピングを利用するようにメソッドをアローで追記します
Route::resource('hoge', 'FugeController')->parameter('hoge', 'article'); // 第一引数に変更対象のbind名、第二引数に変更後のbind名
これで /hoge/{article}
が実現します
便利。すごい。