ルートモデルバインディングを備えたよりクリーンなLaravelコントローラー
序章
ウェブサイトを構築するためのフレームワークまたは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;
});
結論
ルートモデルバインディングの詳細については、ドキュメントを参照してください。
うまくいけば、この小さいながらもきちんとした機能により、プロジェクトのコードを数行節約し、コントローラーをよりクリーンにすることができます。