php 48 lines · 8 steps

Auto-generating unique slugs in Laravel

An Eloquent model that keeps a URL-friendly slug in sync with its title and guarantees it's unique.

Explained by highlit
1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Model;
6use Illuminate\Support\Str;
7 
8class Article extends Model
9{
10 protected $fillable = ['title', 'body', 'slug'];
11 
12 protected static function booted(): void
13 {
14 static::saving(function (Article $article) {
15 if ($article->slug && ! $article->isDirty('title')) {
16 return;
17 }
18 
19 $article->slug = $article->generateUniqueSlug($article->title);
20 });
21 }
22 
23 protected function generateUniqueSlug(string $source): string
24 {
25 $base = Str::slug($source);
26 $slug = $base;
27 $suffix = 1;
28 
29 while ($this->slugExists($slug)) {
30 $slug = "{$base}-" . $suffix++;
31 }
32 
33 return $slug;
34 }
35 
36 protected function slugExists(string $slug): bool
37 {
38 return static::query()
39 ->where('slug', $slug)
40 ->when($this->exists, fn ($query) => $query->whereKeyNot($this->getKey()))
41 ->exists();
42 }
43 
44 public function getRouteKeyName(): string
45 {
46 return 'slug';
47 }
48}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Model lifecycle hooks let you enforce derived data at the exact moment a record is persisted.
  2. 2Suffixing and re-querying is a simple, reliable way to resolve collisions in a unique column.
  3. 3Overriding the route key lets you expose meaningful URLs instead of raw numeric IDs.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Auto-generating unique slugs in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code