Skip to main content

Nested (hierarchical) slugs

Compose ancestor slugs into the URL path — /electronics/phones/iphone_TOKEN. Override polyslugParent() to point at the parent, and scope uniqueness on the parent key so the same segment can repeat under different parents:

#[Polyslug(source: 'name', scope: 'parent_id')]
class Category extends Model implements Sluggable
{
use HasPolyslug;

public function polyslugParent(): ?Sluggable
{
return $this->parent_id === null ? null : self::find($this->parent_id);
}
}

Route it with a catch-all segment:

Route::polyslug('/{category}', [CategoryController::class, 'show'])->where('category', '.*');

Composed on read, never stored

The path is computed from the ancestors' current slugs each time it is built. Renaming or reparenting an ancestor therefore changes the URL immediately, and the canonical middleware 301s the stale one — with no cascade to run and no stored path column to keep in sync.

That is the whole point of the design: a stored materialized path is a second source of truth, and the moment a background job fails, it disagrees with the slugs it was derived from. Here there is nothing to disagree with.

Read the path directly when you need it:

$category->polyslugPath(); // "electronics/phones/iphone"
$category->polyslugPath('de'); // the German path
$category->polyslugPath('de', 10); // with a tighter recursion bound

Recursion is depth-bounded — 20 levels by default — so an accidental parent cycle stops rather than looping forever.

Scope on the parent key

Without scope: 'parent_id', phones under Electronics and phones under Accessories would collide and the second would become phones-2. Scoping on the parent is what makes each level's names independent. See Uniqueness and scope.

Combining with id-less URLs

Nested composes with idLess, which gives a fully readable path with no token at all — the natural shape for a documentation tree or a category hierarchy whose segments are already unique per parent.

The e-commerce and real-estate recipes both build on this.