composer require oleaass/laravel-sluggable in your project root.Sluggable trait to your Eloquent model (e.g., Post).getSlugOptions() in your model to specify the slug source (e.g., title).Generate a slug for a new Post model:
$post = Post::create(['title' => 'Hello World']);
echo $post->slug; // Output: hello-world
getSlugOptions() to customize slug behavior.slug column (default) or specify a custom column via dest option.allowDuplicate is false (default) to enforce uniqueness.Basic Slug Generation
// Auto-generate slug on create (default)
$post = Post::create(['title' => 'Laravel Sluggable']);
Manual Slug Override
// Skip auto-generation and set slug manually
$post = Post::create([
'title' => 'Custom Slug Post',
'slug' => 'custom-slug-overridden'
]);
Dynamic Slug Sources
// Use multiple fields (e.g., title + category)
public function getSlugOptions(): array {
return [
'source' => function () {
return strtolower($this->title . '-' . $this->category->name);
}
];
}
Update Behavior
// Update slug on title change (requires `onUpdate: true`)
$post->update(['title' => 'Updated Title']);
echo $post->fresh()->slug; // Updated slug
public function rules() {
return [
'slug' => 'required|unique:posts,slug,' . $this->post->id,
];
}
public function toArray($request) {
return [
'slug' => $this->slug,
// ...
];
}
Route::get('/posts/{slug}', [PostController::class, 'show']);
Duplicate Slugs
allowDuplicate: true, slugs may collide.allowDuplicate: false (default) and handle conflicts in getSlugOptions:
'source' => function () {
$slug = Str::slug($this->title);
return Post::where('slug', $slug)->exists() ? $slug . '-1' : $slug;
}
Case Sensitivity
Slug and slug as different.getSlugOptions:
'source' => function () {
return Str::lower(Str::slug($this->title));
}
Performance on Update
save() can slow down bulk operations.public function getSlugOptions(): array {
return [
'onUpdate' => false, // Disable slug updates
];
}
Reserved Words
create or update may conflict with Laravel methods.dest column (e.g., url_slug).public function getSlugOptions(): array {
\Log::debug('Slug source:', ['source' => $this->title]);
return ['source' => 'title'];
}
slug column exists and matches the dest option.Custom Slug Logic
Override the generateSlug method in your model:
public function generateSlug(): string {
return parent::generateSlug() . '-custom';
}
Event Listeners Trigger events for slug changes:
protected static function booted() {
static::saved(function ($model) {
if ($model->wasChanged('slug')) {
event(new SlugUpdated($model));
}
});
}
Testing Mock slug generation in tests:
$post = Post::create(['title' => 'Test']);
$this->assertEquals('test', $post->slug);
How can I help you explore Laravel packages today?