22
16

More than 5 years have passed since last update.

Route::resource(...)で記述されたrouteに対してRoute model bindingする

Last updated at Posted at 2019-01-21

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 に下記のように追記をします

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名を上書き変更する

web.php
Route::resource('hoge', 'FugeController')

という定義があった時、bindされる部分の名前は{hoge}になっています
これを{hoge}から{article}に変更します

パラメータのマッピングを上書きます

Providers/RouteServiceProvider.php
class RouteServiceProvider extends ServiceProvider
{
    public function boot()
    {
        parent::boot();
        ResourceRegistrar::setParameters(['hoge' => 'article']); // 第一引数に変更対象のbind名、第二引数に変更後のbind名
    }
}

そしてrouteの方で
上書きしたマッピングを利用するようにメソッドをアローで追記します

web.php
Route::resource('hoge', 'FugeController')->parameter('hoge', 'article'); // 第一引数に変更対象のbind名、第二引数に変更後のbind名

これで /hoge/{article} が実現します
便利。すごい。

22
16
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
16