Skip to main content

Model API

The HasPolyslug trait provides the implementations; the Sluggable interface is the type your code and the package both depend on.

Reading slugs

MethodReturnsNotes
currentSlug(?string $locale = null)?stringThe current slug for the given (or active) locale, or null.
slugLocales()list<string>The locales that currently have a slug.
polyslugAddressLocales()list<string>Only on a model implementing ProvidesAddressLocales — the locales it is served under, when that differs from the locales it holds a slug for.
slugHistory(?string $locale = null)list<string>Superseded slugs for a locale, newest first.
slugs()MorphManyThe raw slug rows, for when you need more than the helpers give.

Route keys and paths

MethodReturnsNotes
getRouteKey()stringLaravel's hook; returns the canonical route key so route() and url() are correct by default.
polyslugRouteKey(?string $locale = null)stringThe {slug}_{encodedId} key for the given (or active) locale — or the slug/path alone, with no delimiter and no token, on an idLess model.
polyslugRouteKeyForLocale(string $locale)stringThe same for an explicit locale — never reads the ambient app locale. Use it in sitemaps, queued jobs, feeds and CLI.
polyslugPath(?string $locale = null, int $maxDepth = 20)stringThe slash-joined slug path (ancestors plus own) for a nested model; the own slug when not nested.
shortLink(?string $locale = null)stringA stable short-link token for this model and locale.

Preloading

MethodReturnsNotes
static polyslugPreload(iterable $models)voidWarms the identity tokens for a whole set in one round trip — the companion to eager-loading slugs. See rendering a list of links.

The only static on this list, because it works on a set rather than on one model:

$pages = Page::query()->with('slugs')->paginate();
Page::polyslugPreload($pages);

Write it unconditionally. On an encoder that derives its token from the key alone — Sqids, UUID, ULID, the raw key — it is a no-op, and deliberately a silent one: an optimization hint you can only write after learning which encoder is configured is a configuration question in disguise. Models are grouped by their resolved encoder, so a set whose members carry different #[Polyslug(encoder: ...)] attributes is still correct. Anything in the iterable that is not an instance of the model you called it on is skipped rather than fataling — a hint must not be able to break a render.

Writing slugs

MethodReturnsNotes
setSlug(string $locale, ?string $source = null)voidWrite the current slug for a locale from the given source, or from the model's own source. Bypasses immutable.
polyslugSync(?string $locale = null)voidGenerate or refresh the current slug from the model's source. Idempotent, and a no-op when a current slug exists and either the source is unchanged or the model is immutable.
polyslugOnDeleted()voidApply the delete policy: cascade slug rows on hard or force delete, release on soft-delete when configured.

Multilingual output

MethodReturnsNotes
polyslugUrls(callable $urlUsing)array<string, string>An absolute URL per locale that has a current slug and is routable.
hreflangLinks(callable $urlUsing, ?string $xDefault = null)array<string, string>The above plus a reciprocal, self-referential x-default. Empty when there are no URLs.
hreflangTags(callable $urlUsing, ?string $xDefault = null)HtmlStringRendered <link rel="alternate" hreflang="…"> tags.
sitemapAlternateTags(callable $urlUsing, ?string $xDefault = null)HtmlStringRendered <xhtml:link rel="alternate" hreflang="…"> alternates for a sitemap entry.

The callable receives (string $locale, string $routeKey) and returns an absolute URL.

Hooks you override

MethodDefaultPurpose
polyslugResolveQuery(Builder $query): Builderno-opThe resolution gate. Constrain which rows a slug may resolve to.
polyslugIsRoutable(?string $locale = null): booltrueThe output filter. Keep a model or locale out of hreflang sets and sitemaps.
polyslugParent(): ?SluggablenullThe parent in a nested hierarchy.
polyslugSupersededBy(): ?SluggablenullThe successor a superseded model redirects to.
polyslugIsGone(): boolfalseWhether the model is permanently gone.
polyslug(): PolyslugConfig(not implemented)Only on models implementing ConfiguresPolyslugcomputed configuration.
polyslugResolutionScope(): ?arraynullThe scope a slug-only lookup happens in, as column => value. Only needed when the scope lives in the URL rather than the environment — see Slug-only URLs.
polyslugRobotsDirective(?string $locale = null): string|array'none'The robots directive a gated locale gets — see laravel/head integration. Only consulted when polyslugIsRoutable() said no, and must still contain noindex or none.
polyslugReservedWords(array $inherited): arrayreturns $inheritedFilter, replace or clear the reserved words this model inherits. Offered the model's own reserved, polyslug.reserved.global and the route-derived words.

Gate-respecting lookup

polyslugResolveByKey(mixed $key): ?static resolves this model type by primary key through polyslugResolveQuery(), so tenant and visibility scoping applies on every path — including /go short links. Prefer it over find() anywhere a gated lookup is what you mean.

polyslugResolveSelf(): ?static re-resolves this instance through its own gate: the same row when the caller may see it, null when it may not. On a model that came from route binding it is a no-op — binding already went through the gate. It is for a model you obtained some other way and are about to disclose something about, which is exactly what the package does with a polyslugSupersededBy() successor before rendering its route key into a Location header.

Blade

@polyslugHreflang($model, $resolver)

The directive shorthand for hreflangTags().