Originally published on laravel.learnio.dev — part of the Laravel best practices course. View this lesson on the original site.

Resolve models directly from route parameters

Route model binding lets Laravel fetch model instances for you from route segments, so controller code stays cleaner.

Implicit binding in one step

Route:

use App\Http\Controllers\PostController;

Route::get('/posts/{post}', [PostController::class, 'show'])
    ->name('posts.show');

Controller:

use App\Models\Post;

public function show(Post $post)
{
    return view('posts.show', ['post' => $post]);
}

If no matching model is found, Laravel returns a 404 automatically.

Use custom keys when IDs are not ideal

Route-level custom key:

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

Model-level default key:

public function getRouteKeyName(): string
{
    return 'slug';
}

Choose route-level when only some routes use slugs, model-level when all should.

Customise missing model behaviour

Default behaviour is 404, but you can redirect:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;

Route::get('/locations/{location:slug}', [LocationController::class, 'show'])
    ->name('locations.show')
    ->missing(function (Request $request) {
        return Redirect::route('locations.index');
    });

Bad way vs better way

Bad way (manual lookup in every action):

public function show(string $id)
{
    $post = Post::findOrFail($id);

    return view('posts.show', ['post' => $post]);
}

Better way (binding in method signature):

public function show(Post $post)
{
    return view('posts.show', ['post' => $post]);
}

Why better:

  • less repetitive lookup code,
  • standard 404 behaviour is automatic,
  • clearer action signatures and easier tests.

Practical binding checklist

  • keep route parameter names and method variable names aligned,
  • use custom keys only where needed,
  • avoid extra findOrFail calls when model is already injected,
  • use missing() when UX needs a redirect instead of 404.

Binding flow

flowchart LR
  A[Route parameter value] --> B[Laravel binding resolver]
  B --> C[Model instance]
  C --> D[Controller action]
  B --> E[404 or missing handler]

Next, you will extend this with custom binding strategies for advanced lookup rules.

From Laravel best practices — Routing, Controllers & Request Flow