0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravelのルートモデルバインディングで実装をシンプルに!

0
Last updated at Posted at 2025-10-15

(気付けば前回の投稿から丸1年経ってた・・・)

皆さん、Laravel使っていますか?
筆者は現在Typescriptがメインの案件についているのですが
たまにLaravelも触ります。

リクエストを受けてからデータの存在チェックをする、なんてのはよくあるケースなんですが
今回初めて知ったものがあるのでメモがてらアウトプットします。

従来のよくある実装パターン

例えば「記事id(post_id)」から記事情報を取得し、詳細データを画面に表示する機能があるとします。

class PostController extends Controller
{
  public function edit(PostEditRequest $request)
  {
    $detail = Post::where('id', $request->post_id)->first();
    return view('post', ['data' => $detail]);
  }
}

と書いておいて、FormRequest側で

class PostEditRequest extends FormRequest
{
  public function rules()
  {
    return [
      'post_id' => 'required|exists:posts,id'
    ];
  }
}

みたいな感じ。あるいは、ルーティング側で

Route::get('/post/{id}', [PostController::class, 'edit']);

のようにして

class PostController extends Controller
{
  public function edit(int $id)
  {
    $detail = Post::where('id', $id)->first();
    if (is_null($detail)) {
      // エラーで返す処理
    }
    return view('post', ['data' => $detail]);
  }
}

のようなパターンもあるでしょう。

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

今回知ったのは「ルートモデルバインディング」という仕組みで
結構前のバージョンから存在しているようです。(何故知らんのだ?というツッコミはお控えくださいw)

これを使うと

Route::get('/post/{post}', [PostController::class, 'edit']);

のようにルーティング設定しておき

class PostController extends Controller
{
  public function edit(Post $post)
  {
    return view('post', ['data' => $post]);
  }
}

という風にすれば、Controllerに入った時点でEloquent Modelのインスタンスができるんですね~。
しかも存在しないidの場合、404エラーを返してくれるというオマケ付き!

主キー以外のカラムで検索する

さらに

Route::get('/post/{post:post_uuid}', [PostController::class, 'edit']);

のように書けば他のカラムに対して検索し、あればController処理を行うし、無ければ404になる。

idのバリデーションチェックをしたいけど、FormRequestを実装するのは大変で
シンプルな実装でも良い場合は、こちらの手法が使えるかもしれませんね。

リレーション関係のモデルをバインディングする

おまけですが、リレーション関係にあるデータをルートモデルバインディングで拾う場合
例えば「会社(Company)」に紐づく「従業員(Employee)」のデータを照会するようなケース

Route::get('/company/{company}/employee/{employee}', [EmployeeController::class, 'detail'])->scopeBindings();

とすればcompanyに紐づくemployeeのモデルを持った状態でController処理を行えます。
受け取り方は

class EmployeeController extends Controller
{
  public function detail(Company $company, Employee $employee)
  {
    // 何かの処理
  }
}

ただ、この方法を使う場合は、LaravelのEloquent ORMが親子関係を理解する必要があるため
予めCompanyモデルに

class Company extends Model
{
  public function employees(): HasMany
  {
    return $this->hasMany(Employee::class);
  }
}

のようにリレーショナルメソッドを用意しておく必要がある。
(他の方法でも親子関係を構築すればできるけど、今回は割愛)

まとめ

  • ルートモデルバインディングを使うとControllerに入った時点でModelインスタンスがある状態になる
    • バリデーション実装が緩くても良いならシンプルに実装できる
    • 存在しないデータの場合は自動で404エラーを返してくれる
  • 主キー以外のカラムでも検索できる
  • 親子関係のModelもscopeBindings()を使えばルートモデルバインディングできる
    • これにより、指定されたemployeeが本当にそのcompanyに属しているかを自動チェックしてくれる

リソース取得と存在確認のそれぞれのアプローチ

注:FormRequestとルートモデルバインディングはそれぞれ異なる概念で、併用が可能です。
ここで伝えたいのは、あくまで同じような処理(リソース取得・存在確認)に対し、それぞれどういうアプローチで処理するかについてです

  • FormRequestを使う場合: 複雑なバリデーションルールがある、カスタムエラーメッセージが必要
  • ルートモデルバインディングを使う場合: シンプルな存在チェックだけで十分、コードをスッキリさせたい

如何だったでしょうか?
実装方法の選択肢が増えると、状況に応じた最適な実装ができるようになりますね。
皆さんもぜひ試してみてください!

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?