DEV Community

yone3
yone3

Posted on • Edited on

Simplify Your Laravel Code with Route Model Binding!

Hey everyone! Are you using Laravel?

I'm currently working mainly with TypeScript, but I also touch Laravel from time to time.

Checking if data exists after receiving a request is a pretty common scenario. I recently discovered something new (to me at least!), so I wanted to share it with you all.

Traditional Implementation Patterns

Let's say you have a feature that retrieves post information using a post_id and displays the details on the screen.

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

And in the FormRequest:

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

Alternatively, you might set up routing like this:

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

And handle it in the controller:

class PostController extends Controller
{
  public function edit(int $id)
  {
    $detail = Post::where('id', $id)->first();
    if (is_null($detail)) {
      // Handle error
    }
    return view('post', ['data' => $detail]);
  }
}
Enter fullscreen mode Exit fullscreen mode

These are pretty standard patterns, right?

What is Route Model Binding?

What I recently learned about is "Route Model Binding" - apparently it's been around for quite a while. (Please don't judge me for not knowing about it! 😅)

With Route Model Binding, you can do this:

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

And in your controller:

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

By the time your code reaches the controller, you already have an Eloquent Model instance!

Plus, if the ID doesn't exist, Laravel automatically returns a 404 error. How convenient!

Searching by Non-Primary Key Columns

You can also do this:

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

This searches by a different column. If found, the controller processes it; if not, you get a 404.

If you need ID validation but implementing a FormRequest feels like overkill, this approach might be perfect for your use case.

Binding Related Models

Here's a bonus: you can also use Route Model Binding with related data.

For example, let's say you want to retrieve Employee data that belongs to a specific Company:

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

This ensures you have the employee model that's associated with the company when the controller processes the request.

In your controller:

class EmployeeController extends Controller
{
  public function detail(Company $company, Employee $employee)
  {
    // Do something
  }
}
Enter fullscreen mode Exit fullscreen mode

For this to work, Laravel's Eloquent ORM needs to understand the parent-child relationship. You'll need to define the relationship in your Company model:

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

(There are other ways to establish these relationships, but I'll skip those for now.)

Summary

  • Route Model Binding gives you a Model instance by the time you reach the controller
    • Great for simple implementations where you don't need complex validation
    • Automatically returns a 404 error for non-existent data
  • You can search by columns other than the primary key
  • Parent-child relationships can be bound using scopeBindings()
    • This automatically verifies that the specified employee actually belongs to that company

Choosing Your Approach for Resource Existence Checks

Note: This comparison focuses on how to fetch the resource from the database. Of course, you can still use FormRequest for other validation rules (like validating request parameters) even when using Route Model Binding for fetching the model.

  • Use FormRequest when: You have complex validation rules or need custom error messages
  • Use Route Model Binding when: Simple existence checks are sufficient and you want simple code

Wrapping Up

Route Model Binding is simple and incredibly convenient! Laravel still has so many features I haven't discovered yet. I'll share more as I find them!

Top comments (3)

Collapse
 
xwero profile image
david duymelinck

I'm glad you discovered route model binding.

You can use route model binding and FormRequest together, they are not mutually exclusive. The model binding can handle the path parameters, while the FormRequest can handle the form input.

Collapse
 
yone3 profile image
yone3

Wow — I wasn’t expecting a comment, so getting yours genuinely made my day! 🙌
You’re absolutely right: route model binding and FormRequest serve different purposes and can absolutely be used together.
Calling it “When to Use What” was misleading — thanks for pointing that out. I’ve added a note in the article to clarify it.
Thanks again for your insight!

Collapse
 
xwero profile image
david duymelinck

No problem, that is why we post things here. Learning from each other.