spatie/laravel-sluggable
Automatically generate unique, URL-friendly slugs for Laravel Eloquent models on save. Configure slug sources and target fields via a simple HasSlug trait and SlugOptions, with built-in uniqueness handling using Laravel’s Str::slug.
/blog/my-article instead of /blog/123). Aligns with modern web best practices for content addressability.HasSlug), adhering to the Single Responsibility Principle (SRP). Business logic (e.g., slug sources, uniqueness rules) is configurable via SlugOptions.scope_id).laravel-translatable integration).Str::slug for efficiency.Str::slug, Eloquent events). No conflicts with existing Laravel features (e.g., implicit model binding).slug column (e.g., string(255)), which is trivial to add.saving event, ensuring atomicity (slug generation fails if model save fails).SlugOptions methods) are additive. Existing implementations remain functional.| Risk Area | Assessment | Mitigation |
|---|---|---|
| Slug Collisions | Default behavior appends -N to duplicates, but custom suffixes may fail. |
Use usingSuffixGenerator for deterministic uniqueness (e.g., UUIDs). |
| Migration Complexity | Requires adding a slug column to existing tables. |
Scripted migration with downtime planning (if applicable). |
| Locale/Translation Overhead | HasTranslatableSlug adds complexity for multi-lingual apps. |
Test thoroughly with laravel-translatable; consider caching slugs. |
| Route Binding Conflicts | Custom getRouteKeyName() may clash with existing routes. |
Validate slug uniqueness in route constraints (e.g., where('slug', '[a-z0-9\-]+')). |
| Performance at Scale | High write volume could stress slug uniqueness checks. | Benchmark with allowDuplicateSlugs() for read-heavy workloads. |
laravel-translatable integration needed? If so, are there performance concerns for large datasets?usingSuffixGenerator?string).findBySlug).Model::dispatchSync()).Phase 1: Pilot Model
slug column to a non-critical model (e.g., BlogPost).HasSlug trait and test slug generation/route binding.tinker tests for 10K inserts).Phase 2: Core Models
Product, Article).Route::get('/posts/{post:slug}', ...)).Phase 3: Full Adoption
/posts/123) via middleware redirects.composer.json for min version).SlugOptions).SlugOptions for app-specific rules (e.g., regex patterns).slug column to all target tables (use a migration factory for consistency).Schema::table('posts', function (Blueprint $table) {
$table->string('slug')->unique()->after('title');
});
HasSlug trait and getSlugOptions() to models.getRouteKeyName('slug') for implicit binding.{id} with {model:slug} in route definitions.where('slug', '[\w\-]+')).findBySlug.config('app.use_slug_routes')).preventOverwrite() is disabled). Plan for:
Post::where('title', 'old-name')->update(['slug' => Str::slug('new-name')])).Product use name + category").SlugOptions to emit events).getRouteKeyName() is consistent across models.Str::slug('café') → cafe).generateSlug() method to models for manual testing.try-catch in HasSlug trait).DB::enableQueryLog()).slug columns.cache()->remember("slug:{$model->id}", ...)).findBySlug is O(1) with proper indexing. No scaling concerns.| Failure Scenario | Impact | Mitigation |
|---|---|---|
| Database connection failure | Slugs not generated/saved. | Retry logic in HasSlug trait (e.g., save() with exponential backoff). |
| Slug collision rate > 1% | Performance degradation. | Monitor via DB::listen(); alert on high collision counts. |
| Route binding mismatch | 404 errors for valid slugs. | Validate getRouteKeyName() in tests; use Route::bind() as fallback. |
| Custom suffix generator fails | Duplicate slugs. | Fallback to default -N suffix (e.g., try-catch in usingSuffixGenerator). |
| Migration backfill errors |
How can I help you explore Laravel packages today?