pushery / polyslug-for-laravel
Polymorphic, multilingual routable identity for Eloquent — leak-safe IDs, per-locale slugs, and hreflang out of the box.
Requires
- php: ^8.4
- ext-json: *
- illuminate/collections: ^13.0
- illuminate/console: ^13.0
- illuminate/container: ^13.0
- illuminate/contracts: ^13.0
- illuminate/database: ^13.0
- illuminate/filesystem: ^13.0
- illuminate/http: ^13.0
- illuminate/routing: ^13.0
- illuminate/support: ^13.0
- illuminate/view: ^13.0
- sqids/sqids: ^0.5.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pao: ^1.1
- laravel/pint: ^1.0
- orchestra/testbench: ^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-browser: ^4.3
- pestphp/pest-plugin-laravel: ^4.0
- pestphp/pest-plugin-type-coverage: ^4.0
- rector/rector: ^2.0
- spaze/phpstan-disallowed-calls: ^4.12
This package is auto-updated.
Last update: 2026-07-26 05:52:43 UTC
README
Polyslug
Polymorphic, multilingual routable identity for Eloquent. Pretty URLs that are safe to expose, safe to rename, and correct across languages — with leak-free IDs, self-healing canonical redirects, and hreflang built in.
/blog/laravel-routing-explained_aB3xK
└──────── slug ─────────┘ └id─┘
changeable, localized, SEO-friendly stable, opaque, resolves the model
A slug should be free to change. A URL should never break. Those two goals usually fight each other. Polyslug settles the fight by splitting a URL into two independent parts — a human-readable slug (pretty, per-locale, editable) and a stable opaque identity (an encoded token that resolves the model). Rename the slug all you like: the identity still resolves, and old URLs redirect themselves to the new canonical one.
Install
composer require pushery/polyslug-for-laravel php artisan migrate
Mark a model as sluggable, point a route at it, and you are done:
#[Polyslug(source: 'title')] class Page extends Model implements Sluggable { use HasPolyslug; } Route::polyslug('/pages/{page}', [PageController::class, 'show'])->name('pages.show');
Rename the page and the old URL 301s to the new one, by itself.
Why Polyslug?
- 🔒 Leak-safe IDs by default. URLs carry an encoded token, not
/pages/1523. No exposed row counts, no enumerable primary keys. The encoder is pluggable — Sqids, UUID, ULID, or your own. - ♻️ Self-healing URLs. Rename freely. A stale slug on a
GET/HEADrequest is301-redirected to the current canonical URL automatically — no redirect tables to hand-maintain, no dead links, no lost link equity. - 🌍 Multilingual with hreflang out of the box. One slug per locale, and a
reciprocal
hreflangset (plusx-default) generated from the same resolver that builds your canonical URL — so they can never drift apart. - 🧩 Polymorphic routing. Serve every content type — pages, articles, products —
through a single
{type}/{polyslug}route and one registry. - 🧭 Stable resolution. Route-model binding decodes the identity, not the slug, so
a mistyped or outdated slug still finds the right model (then redirects). An unknown
or malformed token is a clean
404— never a fuzzy match. - 🏢 Scoped uniqueness. Uniqueness can be scoped per tenant, per locale, per category — whatever columns you name.
- 🗂️ History, events & immutability. Superseded slugs are kept so old URLs keep
resolving; a
SlugChangedevent fires on every change; slugs can be frozen. - ✅ Serious about correctness. PHPStan at
max, 100% line + type coverage, and a mutation-tested suite that runs on SQLite, PostgreSQL, and MySQL 8.4.
Documentation
Full docs at docs.pushery.com/polyslug-for-laravel.
- Installation — requirements, the migration, publishing
- Quick start — a sluggable model and a self-healing route
- Features — encoders, hreflang, nested paths, sitemaps, short links
- Recipes — twelve app shapes wired end to end
- Reference — every config key, option, command, event and contract
Requirements
- PHP 8.4+
- Laravel 13+
- PostgreSQL, MySQL 8.4+, or SQLite — the uniqueness guarantees are enforced natively on each, and the full suite runs against all three. So Polyslug works on Laravel Cloud (serverless Postgres + MySQL 8.4 LTS) with no extra configuration.
Security
Please review the security policy and report vulnerabilities privately rather than opening a public issue.
Built by Pushery
This package is built and maintained by Pushery — a Berlin-based studio building Laravel applications, SaaS products, and open-source tools.
Building a Laravel UI? WireKit, Pushery's open-source Livewire component kit, gives you a polished component library out of the box. Browse the rest of our work at pushery.com.
License
The MIT License (MIT). See LICENSE for details.