Using the HasSlug trait | laravel-sluggable | Spatie

 SPATIE

  Laravel Sluggable
====================

spatie.be/open-source

  [Docs](https://spatie.be/docs)  [Laravel-sluggable](https://spatie.be/docs/laravel-sluggable/v4)  Basic-usage  Using the HasSlug trait

 Version   v4

 Other versions for crawler [v4](https://spatie.be/docs/laravel-sluggable/v4)

  Using the HasSlug trait
- [ Introduction ](https://spatie.be/docs/laravel-sluggable/v4/introduction)
- [ Requirements ](https://spatie.be/docs/laravel-sluggable/v4/requirements)
- [ Installation &amp; setup ](https://spatie.be/docs/laravel-sluggable/v4/installation-setup)
- [ Translatable slugs ](https://spatie.be/docs/laravel-sluggable/v4/translatable-slugs)
- [ Laravel Boost skill ](https://spatie.be/docs/laravel-sluggable/v4/laravel-boost-skill)
- [ Changelog ](https://spatie.be/docs/laravel-sluggable/v4/changelog)
- [ Questions and issues ](https://spatie.be/docs/laravel-sluggable/v4/questions-issues)
- [ Support us ](https://spatie.be/docs/laravel-sluggable/v4/support-us)
- [ Upgrading ](https://spatie.be/docs/laravel-sluggable/v4/upgrading)

Basic usage
-----------

- [ Generating your first slug ](https://spatie.be/docs/laravel-sluggable/v4/basic-usage/getting-started)
- [ Using the Sluggable attribute ](https://spatie.be/docs/laravel-sluggable/v4/basic-usage/using-the-attribute)
- [ Using the HasSlug trait ](https://spatie.be/docs/laravel-sluggable/v4/basic-usage/using-the-has-slug-trait)
- [ Finding models by slug ](https://spatie.be/docs/laravel-sluggable/v4/basic-usage/finding-models-by-slug)
- [ Self-healing URLs ](https://spatie.be/docs/laravel-sluggable/v4/basic-usage/self-healing-urls)

Advanced usage
--------------

- [ Combining multiple source columns ](https://spatie.be/docs/laravel-sluggable/v4/advanced-usage/source-fields)
- [ Tuning the uniqueness suffix ](https://spatie.be/docs/laravel-sluggable/v4/advanced-usage/uniqueness)
- [ Overriding the underlying actions ](https://spatie.be/docs/laravel-sluggable/v4/advanced-usage/overriding-actions)

 Using the HasSlug trait
=======================

###  On this page

1. [ A closure as the source field ](#content-a-closure-as-the-source-field)
2. [ Skip slug generation conditionally ](#content-skip-slug-generation-conditionally)
3. [ Scope uniqueness to a subset of rows ](#content-scope-uniqueness-to-a-subset-of-rows)
4. [ A custom suffix generator ](#content-a-custom-suffix-generator)
5. [ Find a model by its slug ](#content-find-a-model-by-its-slug)
6. [ Regenerate a slug on demand ](#content-regenerate-a-slug-on-demand)
7. [ Translatable slugs ](#content-translatable-slugs)
8. [ Self-healing URLs ](#content-self-healing-urls)

The `HasSlug` trait is the long form of the same configuration the `#[Sluggable]` attribute encodes. Add the trait, implement `getSlugOptions()`, and return a `SlugOptions` instance. With nothing else added the model behaves identically to one annotated `#[Sluggable(from: 'title', to: 'slug')]`.

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;

class Post extends Model
{
    use HasSlug;

    public function getSlugOptions(): SlugOptions
    {
        return SlugOptions::create()
            ->generateSlugsFrom('title')
            ->saveSlugsTo('slug');
    }
}
```

Reach for the trait when you need any of the following. None can be expressed through static attribute arguments.

A closure as the source field
-----------------------------------------------------------------------------------------------------------------------------------------------

Compute the slug from arbitrary model state, including related models, by passing a closure to `generateSlugsFrom()`.

```
return SlugOptions::create()
    ->generateSlugsFrom(fn (Post $post) => "{$post->author->name} {$post->title}")
    ->saveSlugsTo('slug');
```

Skip slug generation conditionally
--------------------------------------------------------------------------------------------------------------------------------------------------------------

`skipGenerateWhen()` accepts a closure that runs on every save. Returning `true` leaves the slug column untouched for that save.

```
return SlugOptions::create()
    ->generateSlugsFrom('title')
    ->saveSlugsTo('slug')
    ->skipGenerateWhen(fn () => $this->state === 'draft');
```

Scope uniqueness to a subset of rows
--------------------------------------------------------------------------------------------------------------------------------------------------------------------

`extraScope()` narrows the uniqueness check, so two records can share a slug as long as they differ on the scope columns.

```
return SlugOptions::create()
    ->generateSlugsFrom('title')
    ->saveSlugsTo('slug')
    ->extraScope(fn ($query) => $query->where('tenant_id', $this->tenant_id));
```

A custom suffix generator
-----------------------------------------------------------------------------------------------------------------------------------

Replace the default `-1`, `-2`, ... collision suffix with your own. The closure receives the base slug and the collision iteration.

```
return SlugOptions::create()
    ->generateSlugsFrom('title')
    ->saveSlugsTo('slug')
    ->usingSuffixGenerator(fn (string $slug, int $iteration) => bin2hex(random_bytes(4)));
```

Find a model by its slug
--------------------------------------------------------------------------------------------------------------------------------

The trait adds a static `findBySlug()` helper so you don't have to hand-write the `where()`.

```
$post = Post::findBySlug('hello-world');
```

See [Finding models by slug](/docs/laravel-sluggable/v4/basic-usage/finding-models-by-slug) for the full signature.

Regenerate a slug on demand
-----------------------------------------------------------------------------------------------------------------------------------------

The trait exposes a public `generateSlug()` method that forces regeneration outside the normal save lifecycle. Call `save()` afterwards to persist the new value.

```
$post->generateSlug();
$post->save();
```

Translatable slugs
--------------------------------------------------------------------------------------------------------------

`HasTranslatableSlug` (which uses `HasSlug` under the hood) generates one slug per locale. See [Translatable slugs](/docs/laravel-sluggable/v4/translatable-slugs).

Self-healing URLs
-----------------------------------------------------------------------------------------------------------

Self-healing requires the trait so it can override `getRouteKey()` and `resolveRouteBinding()`. The feature itself can be enabled through the attribute (`selfHealing: true`) or through the slug options (`->selfHealing()`). See [Self-healing URLs](/docs/laravel-sluggable/v4/basic-usage/self-healing-urls).

 A good
match?
-------------

### What we do best

- All things Laravel
- Custom frontend components
- Building APIs
- AI-powered features
- Simplifying things
- Clean solutions
- Integrating services

### Not our cup of tea

- WordPress themes
- Cutting corners
- Free mockups to win a job
- "Just execute the briefing"

 In short: we'd like to be a **substantial part** of your project.

 [ Get in touch via email ](mailto:info@spatie.be?subject=A%20good%20match%21&body=Tell%20us%20as%20much%20as%20you%20can%20about%0A-%20your%20online%20project%0A-%20your%20planning%0A-%20your%20budget%0A-%20%E2%80%A6%0A%0AAnything%20that%20helps%20us%20to%20start%20straightforward%21)
