Route Model Binding
Automatically resolving Model from route parameter
Route Model Binding allows Laravel to automatically resolve Model from route parameter. This feature makes code cleaner and reduces need for manual queries.
Types of Route Model Binding:
1. Implicit Binding: Laravel automatically finds Model based on type-hint. Uses convention over configuration.
2. Explicit Binding: You manually define binding in RouteServiceProvider or boot() method. Used for custom resolution logic.
3. Custom Key Binding: You can use fields other than id for binding. Like email, slug, username.
Benefits:
- Cleaner and more readable code
- Fewer errors with automatic 404
- Reduced boilerplate code
- Type safety with type-hinting
- Automatic validation
- Custom resolution logic
How it Works:
Laravel takes route parameter, finds Model using primary key (or custom key), and automatically returns 404 if not found.
Examples
Implicit Binding
Route::get('/users/{user}', function (User $user) {
return $user;
});
// Laravel automatically:
// 1. Takes 'user' from route parameter
// 2. Finds User model with id = {user}
// 3. Returns 404 if not found
// 4. Injects User instance
Laravel automatically finds User by id from route.
Custom Key Binding
// Using email instead of id
Route::get('/users/{user:email}', function (User $user) {
return $user;
});
// Using slug
Route::get('/posts/{post:slug}', function (Post $post) {
return $post;
});
// Multiple custom keys
Route::get('/users/{user:username}/posts/{post:slug}', function (User $user, Post $post) {
return view('user.post', compact('user', 'post'));
});
Using email instead of id to find User.
Explicit Binding
// In RouteServiceProvider or AppServiceProvider
public function boot()
{
Route::bind('user', function ($value) {
return User::where('username', $value)
->orWhere('email', $value)
->firstOrFail();
});
// Or with custom logic
Route::bind('post', function ($value, $route) {
return Post::where('slug', $value)
->where('published', true)
->firstOrFail();
});
}
// Usage
Route::get('/users/{user}', function (User $user) {
// Uses custom binding
return $user;
});
Defining custom binding to find User by username.
Scoped Binding
// Parent-child relationship binding
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
// Post must belong to user
return $post;
});
// In Post model
public function getRouteKeyName()
{
return 'slug';
}
// In RouteServiceProvider
Route::bind('post', function ($value, $route) {
return $route->parameter('user')
->posts()
->where('slug', $value)
->firstOrFail();
});
Scoped binding that finds post only in user's scope.
Soft Deleted Models
// By default, soft deleted models are excluded
Route::get('/users/{user}', function (User $user) {
// Soft deleted users return 404
return $user;
});
// Include soft deleted models
Route::get('/users/{user}', function (User $user) {
//
})->withTrashed();
// Or in explicit binding
Route::bind('user', function ($value) {
return User::withTrashed()->findOrFail($value);
});
Managing soft deleted models in route model binding.
Custom Route Key Name
<?php
namespace App\Models;
class User extends Model
{
// Use username instead of id for route binding
public function getRouteKeyName()
{
return 'username';
}
// Or getRouteKey() for custom value
public function getRouteKey()
{
return $this->slug;
}
}
// Usage
Route::get('/users/{user}', function (User $user) {
// Automatically uses username
return $user;
});
Defining custom route key name in Model.
Binding with Eager Loading
// In RouteServiceProvider
Route::bind('post', function ($value) {
return Post::with(['author', 'comments', 'tags'])
->where('slug', $value)
->firstOrFail();
});
// Or in model
public function resolveRouteBinding($value, $field = null)
{
return $this->where($field ?? $this->getRouteKeyName(), $value)
->with(['author', 'comments'])
->firstOrFail();
}
Eager loading relationships in route model binding.
Conditional Binding
// In RouteServiceProvider
Route::bind('post', function ($value, $route) {
$query = Post::where('slug', $value);
// Only show published posts for non-admins
if (!auth()->check() || !auth()->user()->isAdmin()) {
$query->where('published', true);
}
return $query->firstOrFail();
});
Conditional binding based on user permissions.
Use Cases
- Reducing boilerplate code in controllers
- Automatic 404 handling for missing models
- Using slug instead of id for SEO-friendly URLs
- Scoped binding for nested resources
- Custom resolution logic for complex queries
- Eager loading relationships in binding
Common Mistakes
- Using id in URL causing security issues (use slug instead)
- Not handling soft deleted models
- N+1 query problem in binding
- Not using eager loading in explicit binding
- Forgetting firstOrFail() causing null return
- Using binding for complex queries that should be in controller
Best Practices
- Use slug instead of id for public URLs
- Use eager loading in explicit binding
- Use scoped binding for nested resources
- Define custom route key name in Model
- Use firstOrFail() for automatic 404
- Keep binding logic in RouteServiceProvider
- Use type-hinting for type safety
Edge Cases
- Binding with soft deleted models
- Binding with multiple custom keys
- Scoped binding with nested relationships
- Binding in queue jobs which have different container instance
- Binding with polymorphic relationships
- Conditional binding based on permissions
Performance Notes
- Using eager loading in binding can solve N+1 problem
- Custom key binding with index can improve performance
- Explicit binding can have overhead if complex query
- Route model binding caching in Laravel 11.x improves performance
Security Notes
- Use slug instead of id for public URLs
- Ensure binding logic checks authorization
- Use scoped binding for nested resources
- Ensure sensitive models are properly protected
Interview Points
- What is the difference between implicit and explicit binding?
- How can you define custom key for binding?
- What is scoped binding and when is it used?
- How can you handle soft deleted models in binding?
- What are the benefits of using route model binding?
- How can you solve N+1 problem in binding?
Version Notes
- Laravel 11.x: Improved performance in route model binding
- Laravel 11.x: Better support for custom route keys
- Laravel 10.x: Improved scoped binding
- Laravel 9.x: Added resolveRouteBinding() method