Route Model Binding
Route model binding automatically resolves Eloquent models from route parameters, eliminating manual lookup code.
Implicit Binding
// Laravel finds the Post by ID automatically
Route::get('/posts/{post}', function (Post $post) {
return view('posts.show', compact('post'));
});
Custom Key Binding
Use a slug instead of ID:
// In the route
Route::get('/posts/{post:slug}', [PostController::class, 'show']);
// Or define it on the model
class Post extends Model
{
public function getRouteKeyName(): string
{
return 'slug';
}
}
Scoped Bindings
Automatically scope child models to their parent:
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
// $post is automatically scoped to the $user
return view('posts.show', compact('user', 'post'));
})->scopeBindings();
Customizing Missing Model Behavior
Route::get('/posts/{post}', [PostController::class, 'show'])
->missing(function () {
return redirect()->route('posts.index')
->with('error', 'Post not found.');
});
API Resources for JSON Responses
Transform models into structured JSON:
php artisan make:resource PostResource
class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'excerpt' => Str::limit($this->body, 150),
'published_at' => $this->published_at?->toISOString(),
'author' => new UserResource($this->whenLoaded('author')),
'tags' => TagResource::collection($this->whenLoaded('tags')),
'links' => [
'self' => route('api.posts.show', $this),
],
];
}
}
// In controller
public function index(): AnonymousResourceCollection
{
return PostResource::collection(
Post::with(['author', 'tags'])->published()->paginate(15)
);
}