ルートモデルバインディングを備えたよりクリーンなLaravelコントローラー

提供:Dev Guides
移動先:案内検索

序章

ウェブサイトを構築するためのフレームワークまたはAPIを構築するためのコンテナー( lumen )としてのLaravelは、開発者が選択するフレームワークとして進化してきました。 Laravelには、多くの機能が追加されています。たとえば、Laravelのイベントを取り上げます。 Laravelのイベントは、以前は単純なpub-subライブラリでしたが、Laravelイベントをクライアントにブロードキャストできるようになり、リアルタイムアプリを作成できるようになりました。

しかし、それは重要なことではありませんが、今日の有名人はLaravelのルートモデルバインディングです。

ルートモデルバインディングとは

Laravelのルートモデルバインディングは、モデルインスタンスをルートに注入するメカニズムを提供します。 まだ意味がはっきりしていませんが、ここに例を示します。 データベースから投稿を取得したい場合は、次のようにすることができます。

...
// the route parameter is the id of the post
// for example http://example.com/posts/53
Route::get('posts/{id}', function ($id) {

  // we have to find the post using the $id
  $post = Post::find($id);

  // if there is no post, 404
  if (!$post) return abort(404);

  // return the view and the post
  return view('post.show', compact('post'));
});
...

この方法をさらに単純化して、

...
// the route parameter is the id of the post
// for example http://awesome.dev/posts/53
Route::get('posts/{id}', function ($id) {

  // find the post or 404 if not found
  $post = Post::findOrFail($id);

  // return the view and the post
  return view('post.show', compact('post'));
});
...

ただし、ルートモデルバインディングは、上記の両方のインスタンスを単純化することにより、余分なキーストロークを取り除くのに役立ちます。

...
// by using $post, we can inject the Post object
Route::get('posts/{post}', function ($post) {

  // we now have access to the $post object! no code necessary

  // return the view and the post
  return view('post.show', compact('post'));
});
...

これは、LaravelにPostモデルを{post}パラメーターが付加されているルートコントローラーに注入するように指示することで可能になります。

Laravelは現在、2種類のルートモデルバインディングをサポートしています。 我々は持っています:

  • 暗黙的なモデルバインディング
  • 明示的なモデルバインディング

注:上記のルートモデルバインディングの例は明示的です。


暗黙的なモデルバインディング

明示的なモデルバインディングを見てきましたが、暗黙的なモデルバインディングの例を次に示します。

Route::get('posts/{post}', function (App\Post $post) {
  // be awesome. enjoy having the $post object
});

Laravelは、Postモデルがコントローラークロージャーに注入されているため、ルートからidパラメーターを取得し、ユーザーの詳細を取得する必要があることを知っているほど賢いです。

投稿へのアクセスは、引き続きhttp://awesome.example.com/posts/24を使用して行われます。

モデルのルートキーの変更

モデルを取得するときに暗黙のモデルバインディングでid以外のデータベース列を使用する場合は、EloquentモデルのgetRouteKeyNameメソッドをオーバーライドできます。

たとえば、idの代わりにslugを使用する場合は、次のように実行できます。

class Post extends Model {
  public function getRouteKeyName() {
    return 'slug';
  }
}

次に、http://awesome.example.com/posts/24の代わりにhttp://awesome.example.com/posts/my-post-slugを使用してルートにアクセスできます。

明示的なモデルバインディング

名前が示すように、urlパラメーターを特定のモデルにバインドするようにLaravelに明示的に指示する必要があります。 これを行うには2つの方法があります。提供されているRouteファサードを使用してパラメーターをモデルにバインドするか、app/Providers/RouteServiceProvider.phpでこのバインドを実行します(この方法が好きです)。

Routeファサードの使用

Routeファサードを使用してパラメーターをモデルにバインドすると、次のようになります。

Route::bind('post', 'App\Post');

また、バインディングにもっと意味を持たせることもできます。たとえば、ドラフトの場合にのみ投稿が必要な場合はどうでしょうか。 そのために、Route::bindの2番目のパラメーターを、ルートパラメーターを値として取るクロージャーに変更できます。

Route::bind('post', function ($value) {
  return App\Post::find($value)->where('status', '=', 'published')->first();
});

RouteServiceProviderを使用する

RouteファサードとRouteServiceProviderクラスの使用の唯一の違いは、バインディングの登録がRouteServiceProviderクラスのbootメソッドで行われることです(場所はapp/Providersディレクトリ)およびbindメソッドは、メソッドに挿入された$routerオブジェクトで呼び出されます。 簡単な例

public function boot(Router $router)
{
  parent::boot($router);

  $router->bind('post', function ($value) {
    return App\Post::find($value)->where('status', '=', 'published')->first();
  });
}

ルートモデルバインディングのカスタム例外

私は多くのAPIを構築しているので、ルートモデルバインディングのカスタム例外は、実際には私のような人々にとってより便利です。 Laravelは私たちがこれを行うための簡単な方法を提供します。 RouteServiceProviderクラスのbootメソッドで、$routerオブジェクトのmodelメソッドを呼び出します。

modelメソッドは3つの引数を取ります。引数は、bindメソッドの引数と似ていますが、新しい例外をスローするクロージャである3番目の引数が新たに追加されています。

$router->model($routeParameter, $modelToBind, function () {
  throw new NotFoundHTTPException;
});

結論

ルートモデルバインディングの詳細については、ドキュメントを参照してください。

うまくいけば、この小さいながらもきちんとした機能により、プロジェクトのコードを数行節約し、コントローラーをよりクリーンにすることができます。