---
title: Use Route Model Binding — No Manual find() in Controllers
impact: MEDIUM
impactDescription: eliminates repetitive find-or-404 boilerplate, centralizes authorization scope
tags: route-model-binding, controller, eloquent, laravel, routing
---

## Use Route Model Binding

Laravel resolves model instances automatically from route parameters. Writing `User::findOrFail($id)` manually in every controller method is redundant boilerplate.

**Wrong:**

```php
// routes/api.php
Route::get('/posts/{id}', [PostController::class, 'show']);

// Controller
public function show(int $id)
{
    $post = Post::findOrFail($id); // unnecessary boilerplate
    $this->authorize('view', $post);
    return PostResource::make($post);
}
```

**Correct:**

```php
// routes/api.php — use the model name as parameter
Route::get('/posts/{post}', [PostController::class, 'show']);

// Controller — Laravel resolves Post automatically, throws 404 if not found
public function show(Post $post)
{
    $this->authorize('view', $post);
    return PostResource::make($post);
}
```

**Custom binding key** (e.g. slug instead of id):

```php
// Model
class Post extends Model
{
    public function getRouteKeyName(): string
    {
        return 'slug'; // /posts/my-post-slug
    }
}
```

**Scoped binding** (child must belong to parent):

```php
// /users/{user}/posts/{post} — post must belong to user
Route::get('/users/{user}/posts/{post}', [PostController::class, 'show'])
    ->scopeBindings();
```

**With eager loading:**

```php
// Customize resolution for eager-loading
Route::bind('post', fn($value) => Post::with(['author', 'tags'])->findOrFail($value));
```
