NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Add the Sluggable contract and the HasSlug trait to a model. With no further configuration, a slug column is generated from the name attribute on create, and collisions get a sequential suffix:

use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Sluggable\Concerns\HasSlug;
use RoundlyConsulting\Sluggable\Contracts\Sluggable;

final class Clinic extends Model implements Sluggable
{
    use HasSlug;   // zero config: `slug` generated from `name`

    protected $fillable = ['name', 'slug'];
}

Clinic::create(['name' => 'Happy Paws'])->slug;   // "happy-paws"
Clinic::create(['name' => 'Happy Paws'])->slug;   // "happy-paws-2"

Both are required: using the trait without implements Sluggable throws InvalidSlugDefinitionException (missingContract). The zero-config column and source come from sluggable.defaults.column and sluggable.defaults.source.

The migration

Two Blueprint macros add the column and a unique index matching the default definition (global uniqueness, trashed rows count as taken):

Schema::create('clinics', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->slug();          // string('slug', 255)
    $table->uniqueSlug();    // unique index clinics_slug_slug_unique
    $table->timestamps();
});

Reading the slug

Besides the attribute itself, the contract gives every sluggable model readers that matter most for locale maps but work on string columns too:

$clinic->slug;            // "happy-paws"
$clinic->currentSlug();   // "happy-paws"
$clinic->slugMap();       // ['*' => 'happy-paws']  (Sluggable::SINGLE_KEY)

slugFor($locale) reads one exact locale and is for locale-map columns only — on a string column it throws. Every reader takes an optional column name; null means the routeKey() column, else the first definition.

With the default options the generated value matches Str::slug() for ordinary input of up to 255 characters, so adding the trait to an existing model keeps the slugs you already have. The differences are covered under Formatting & suffixes.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.