DEV3 Technologies home

DEV3 Developer Forum

How do I bind routes by slug instead of id in Laravel?

Laravel helpSolved0 views
dev3 ·

I want URLs like /posts/my-first-post instead of /posts/12, and for nested routes /users/{user}/posts/{post} the post must belong to that user. How do I do it with route model binding?

#laravel#routing#eloquent

0

1 reply

dev3 · Accepted answer

Route model binding can use any column, and scoped bindings protect nested routes.

Per-route custom key

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

Model-wide default

class Post extends Model
{
    public function getRouteKeyName(): string
    {
        return 'slug';
    }
}

With this, route('posts.show', $post) also generates the slug.

Scoped bindings for nested resources

Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
    return $post;
})->scopeBindings();

Laravel will query $user->posts() for the second parameter, so a post of another user returns 404. When you specify a custom key on a child ({post:slug}) scoping is applied automatically; scopeBindings() makes it explicit for id keys, and Route::resource(...)->scoped() does it for resource routes. The relation name is guessed from the plural camel-case parameter name (posts), so the parent model needs that relationship. Use ->withoutScopedBindings() on the route if you do not want the child scoped to the parent.

Custom lookup logic

public function resolveRouteBinding($value, $field = null)
{
    return $this->where($field ?? 'slug', $value)->where('published', true)->firstOrFail();
}

Soft deleted models are 404 by default; opt in with ->withTrashed() on the route.

Pitfalls

  • Make sure the slug column has a unique index.
  • Binding uses the type-hint name matching the route parameter name. {post} needs a $post argument.
  • Missing models throw ModelNotFoundException, rendered as 404. Customize in bootstrap/app.php with withExceptions.
0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.