Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Laravel Sluggable
https://github.com/spatie/laravel-sluggable
Admin
Laravel Sluggable is a package that automatically generates unique URL-friendly slugs from Eloquent
...
Tokens:
5,584
Snippets:
64
Trust Score:
8.5
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
88.6
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Laravel Sluggable Laravel Sluggable is a PHP package by Spatie that provides automatic URL-friendly slug generation for Laravel Eloquent models. When you save a model, the package automatically generates a unique slug from specified source fields using Laravel's `Str::slug` method, converting spaces to hyphens and handling special characters appropriately. The package offers extensive configuration options including multiple source fields, custom separators, maximum length limits, language-specific slug generation, and scoped uniqueness. It also integrates seamlessly with Spatie's laravel-translatable package to generate localized slugs for multilingual applications. The fluent API makes it easy to customize slug behavior for different use cases while maintaining clean, readable code. ## Installation Install the package via Composer. ```bash composer require spatie/laravel-sluggable ``` ## Basic Model Setup with HasSlug Trait The `HasSlug` trait provides automatic slug generation when creating or updating Eloquent models. You must implement the `getSlugOptions()` method to configure the slug source and destination fields. ```php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Sluggable\HasSlug; use Spatie\Sluggable\SlugOptions; class Article extends Model { use HasSlug; protected $fillable = ['name', 'slug']; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug'); } } // Usage $article = new Article(); $article->name = 'My First Blog Post'; $article->save(); echo $article->slug; // Output: "my-first-blog-post" ``` ## Migration Setup Create a migration with a slug field to store the generated slugs. ```php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateArticlesTable extends Migration { public function up() { Schema::create('articles', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('slug'); $table->timestamps(); $table->unique('slug'); }); } public function down() { Schema::dropIfExists('articles'); } } ``` ## SlugOptions::create() Static factory method to create a new SlugOptions configuration instance with default settings. ```php <?php use Spatie\Sluggable\SlugOptions; $options = SlugOptions::create() ->generateSlugsFrom('title') ->saveSlugsTo('slug'); // Default options: // - generateUniqueSlugs: true // - maximumLength: 250 // - slugSeparator: '-' // - slugLanguage: 'en' // - generateSlugsOnCreate: true // - generateSlugsOnUpdate: true ``` ## generateSlugsFrom() Specifies which model field(s) or callable should be used to generate the slug. Accepts a string, array of strings, or a callable function. ```php <?php use Spatie\Sluggable\SlugOptions; // Single field public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug'); } // Multiple fields public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom(['first_name', 'last_name']) ->saveSlugsTo('slug'); } // "John Doe" -> "john-doe" // Using a callable for custom logic public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom(function ($model) { return "{$model->category->name} {$model->title}"; }) ->saveSlugsTo('slug'); } // Category: "Tech", Title: "New Features" -> "tech-new-features" ``` ## saveSlugsTo() Defines the database field where the generated slug will be stored. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('url_slug'); // Custom field name } // Model with 'url_slug' field $product = Product::create(['name' => 'Super Widget']); echo $product->url_slug; // Output: "super-widget" ``` ## allowDuplicateSlugs() Disables automatic unique slug generation. By default, the package appends a number suffix to ensure uniqueness. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->allowDuplicateSlugs(); } // Without allowDuplicateSlugs (default): // First: "hello-world" // Second: "hello-world-1" // Third: "hello-world-2" // With allowDuplicateSlugs: // First: "hello-world" // Second: "hello-world" // Third: "hello-world" ``` ## slugsShouldBeNoLongerThan() Sets the maximum length for the generated slug. The slug source string is truncated before slug generation. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->slugsShouldBeNoLongerThan(50); } // Long title gets truncated $article = Article::create([ 'name' => 'This Is An Extremely Long Article Title That Would Be Truncated' ]); echo $article->slug; // Output: "this-is-an-extremely-long-article-title-that-woul" // Note: Uniqueness suffix may make it slightly longer than 50 ``` ## usingSeparator() Changes the separator character used in slugs. Default is hyphen (-). ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->usingSeparator('_'); } $item = Item::create(['name' => 'Hello World']); echo $item->slug; // Output: "hello_world" // Duplicates with underscore separator $item2 = Item::create(['name' => 'Hello World']); echo $item2->slug; // Output: "hello_world_1" ``` ## usingLanguage() Sets the language used by Laravel's Str::slug method for language-specific character replacements. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->usingLanguage('de'); } // German umlauts are handled appropriately $article = Article::create(['name' => 'Größe über alles']); echo $article->slug; // Output: "groesse-ueber-alles" // Dutch language public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->usingLanguage('nl'); } ``` ## doNotGenerateSlugsOnCreate() Prevents automatic slug generation when the model is first created. Useful when you want manual control over initial slugs. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->doNotGenerateSlugsOnCreate(); } $article = Article::create(['name' => 'My Article']); echo $article->slug; // Output: null (no slug generated) // Manually trigger slug generation later $article->generateSlug(); $article->save(); echo $article->slug; // Output: "my-article" ``` ## doNotGenerateSlugsOnUpdate() Prevents automatic slug regeneration when the model is updated. Creates permalinks that remain stable. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->doNotGenerateSlugsOnUpdate(); } $article = Article::create(['name' => 'Original Title']); echo $article->slug; // Output: "original-title" $article->name = 'Updated Title'; $article->save(); echo $article->slug; // Output: "original-title" (unchanged) ``` ## preventOverwrite() Prevents slug generation if the slug field already has a value. Useful for preserving manually set slugs. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->preventOverwrite(); } // Slug is generated when field is null $article = Article::create(['name' => 'My Article']); echo $article->slug; // Output: "my-article" // Manually set slug is preserved $article2 = Article::create([ 'name' => 'Another Article', 'slug' => 'custom-slug' ]); echo $article2->slug; // Output: "custom-slug" (not overwritten) ``` ## skipGenerateWhen() Conditionally skips slug generation based on a callable that returns true/false. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->skipGenerateWhen(fn () => $this->state === 'draft'); } // Slug not generated for drafts $draft = Article::create(['name' => 'Draft Post', 'state' => 'draft']); echo $draft->slug; // Output: null // Slug generated for published articles $published = Article::create(['name' => 'Published Post', 'state' => 'published']); echo $published->slug; // Output: "published-post" ``` ## extraScope() Adds additional query constraints when checking for slug uniqueness. Useful for multi-tenant applications or scoped uniqueness. ```php <?php use Spatie\Sluggable\SlugOptions; class Page extends Model { use HasSlug; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->extraScope(fn ($builder) => $builder->where('website_id', $this->website_id)); } } // Slugs are unique within each website $page1 = Page::create(['name' => 'About', 'website_id' => 1]); echo $page1->slug; // Output: "about" $page2 = Page::create(['name' => 'About', 'website_id' => 2]); echo $page2->slug; // Output: "about" (same slug, different website) $page3 = Page::create(['name' => 'About', 'website_id' => 1]); echo $page3->slug; // Output: "about-1" (unique within website 1) ``` ## startSlugSuffixFrom() Sets the starting number for slug uniqueness suffixes. Default is 1. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->startSlugSuffixFrom(2); } Article::create(['name' => 'Hello World']); // slug: "hello-world" Article::create(['name' => 'Hello World']); // slug: "hello-world-2" Article::create(['name' => 'Hello World']); // slug: "hello-world-3" ``` ## useSuffixOnFirstOccurrence() Forces the first slug to also have a suffix, ensuring consistent naming patterns. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->useSuffixOnFirstOccurrence(); } // All slugs get suffixes, including the first Article::create(['name' => 'Example']); // slug: "example-1" Article::create(['name' => 'Example']); // slug: "example-2" Article::create(['name' => 'Example']); // slug: "example-3" ``` ## usingSuffixGenerator() Provides a custom callable for generating uniqueness suffixes instead of incrementing numbers. ```php <?php use Spatie\Sluggable\SlugOptions; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->usingSuffixGenerator( fn (string $slug, int $iteration) => bin2hex(random_bytes(4)) ); } // Random hex suffixes instead of numbers Article::create(['name' => 'Post']); // slug: "post" Article::create(['name' => 'Post']); // slug: "post-a1b2c3d4" Article::create(['name' => 'Post']); // slug: "post-e5f6g7h8" // UUID-based suffix public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug') ->usingSuffixGenerator( fn (string $slug, int $iteration) => substr(md5($slug . $iteration . time()), 0, 8) ); } ``` ## generateSlug() Manually triggers slug regeneration on the model. Call save() afterward to persist. ```php <?php $article = Article::find(1); echo $article->slug; // Output: "old-title" $article->name = 'New Title'; $article->generateSlug(); $article->save(); echo $article->slug; // Output: "new-title" // Useful with doNotGenerateSlugsOnUpdate() $article = Article::create(['name' => 'Original']); // Later, explicitly regenerate $article->name = 'Changed'; $article->generateSlug(); // Manually trigger $article->save(); ``` ## findBySlug() Static method to retrieve a model by its slug value. Supports translatable slugs with fallback locale. ```php <?php // Basic usage $article = Article::findBySlug('my-article'); // With specific columns $article = Article::findBySlug('my-article', ['id', 'name', 'slug']); // With additional query constraints $article = Article::findBySlug('my-article', ['*'], function ($query) { $query->where('published', true); }); // Returns null if not found $article = Article::findBySlug('non-existent'); // $article === null // For translatable models, searches current and fallback locale $article = TranslatableArticle::findBySlug('mon-article'); // Searches 'fr' and 'en' ``` ## Custom Slugs Override the automatically generated slug by setting it manually before saving. ```php <?php $article = Article::create(['name' => 'My Article']); echo $article->slug; // Output: "my-article" // Override with custom slug $article->slug = 'custom-url-path'; $article->save(); echo $article->slug; // Output: "custom-url-path" // Set custom slug on create $article = new Article(); $article->name = 'Article Title'; $article->slug = 'my-custom-slug'; $article->save(); echo $article->slug; // Output: "my-custom-slug" ``` ## Route Model Binding Configure the model to use slugs for Laravel's implicit route model binding. ```php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Sluggable\HasSlug; use Spatie\Sluggable\SlugOptions; class Article extends Model { use HasSlug; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug'); } public function getRouteKeyName(): string { return 'slug'; } } // In routes/web.php Route::get('/articles/{article}', function (Article $article) { return view('articles.show', compact('article')); }); // URL: /articles/my-first-article // Laravel automatically resolves the Article model by slug ``` ## HasTranslatableSlug Trait Integrates with spatie/laravel-translatable to generate unique slugs for each locale. ```php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Sluggable\HasTranslatableSlug; use Spatie\Sluggable\SlugOptions; use Spatie\Translatable\HasTranslations; class Article extends Model { use HasTranslations, HasTranslatableSlug; public array $translatable = ['name', 'slug']; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug'); } } // Create with translations $article = new Article(); $article->setTranslation('name', 'en', 'My Article'); $article->setTranslation('name', 'fr', 'Mon Article'); $article->save(); echo $article->getTranslation('slug', 'en'); // Output: "my-article" echo $article->getTranslation('slug', 'fr'); // Output: "mon-article" ``` ## SlugOptions::createWithLocales() Creates SlugOptions for translatable slugs with a callable generator that receives the locale. ```php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Sluggable\HasTranslatableSlug; use Spatie\Sluggable\SlugOptions; use Spatie\Translatable\HasTranslations; class Article extends Model { use HasTranslations, HasTranslatableSlug; public array $translatable = ['name', 'slug']; public function getSlugOptions(): SlugOptions { return SlugOptions::createWithLocales(['en', 'nl', 'fr']) ->generateSlugsFrom(function ($model, $locale) { return "{$locale}-{$model->getTranslation('name', $locale)}"; }) ->saveSlugsTo('slug'); } } $article = new Article(); $article->setTranslation('name', 'en', 'Hello'); $article->setTranslation('name', 'nl', 'Hallo'); $article->setTranslation('name', 'fr', 'Bonjour'); $article->save(); echo $article->getTranslation('slug', 'en'); // Output: "en-hello" echo $article->getTranslation('slug', 'nl'); // Output: "nl-hallo" echo $article->getTranslation('slug', 'fr'); // Output: "fr-bonjour" ``` ## Translatable Route Model Binding HasTranslatableSlug provides automatic route binding resolution for the current locale. ```php <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Spatie\Sluggable\HasTranslatableSlug; use Spatie\Sluggable\SlugOptions; use Spatie\Translatable\HasTranslations; class Article extends Model { use HasTranslations, HasTranslatableSlug; public array $translatable = ['name', 'slug']; public function getSlugOptions(): SlugOptions { return SlugOptions::create() ->generateSlugsFrom('name') ->saveSlugsTo('slug'); } public function getRouteKeyName(): string { return 'slug'; } } // In routes/web.php Route::get('/articles/{article}', function (Article $article) { return view('articles.show', compact('article')); }); // With app locale set to 'fr': // URL: /articles/mon-article // Resolves article by French slug automatically ``` ## Summary Laravel Sluggable is designed for any Laravel application requiring SEO-friendly URLs, content management systems, blogs, e-commerce platforms, or any system with human-readable identifiers. Common use cases include generating article URLs from titles, creating product slugs from names, building category hierarchies with unique paths, and implementing multilingual content with locale-specific slugs. The package handles edge cases like duplicate names, special characters, and multi-byte strings automatically. Integration follows standard Laravel patterns: install via Composer, add the trait to your models, implement the configuration method, and ensure your database has a slug column. The fluent configuration API allows combining multiple options like maximum length, custom separators, and scope constraints. For existing projects, you can disable automatic generation and trigger it manually, making migration straightforward. The package works seamlessly with Laravel's route model binding for clean controller code and integrates with spatie/laravel-translatable for multilingual applications.