Open source
Sluggable for Laravel
composer require roundly-consulting/sluggable-for-laravelOverview
Single- and multi-language slugs for Eloquent. Store a plain string slug or a json/jsonb locale map with one slug per language, declare any number of slug columns per model, and keep them unique globally or per scope — enforced by engine-native unique indexes. On top come locale-aware route model binding, validation rules, Artisan tooling and an optional slug history that 301-redirects old URLs. Native Laravel only, MIT-licensed, with zero third-party runtime dependencies — and with the default options every slug matches Str::slug() for ordinary input of up to 255 characters.
What you get
Locale-map slugs
Plain string slugs or json/jsonb locale maps — one slug per language, each transliterated by that language’s rules.
Str::slug() compatible
With the default options every slug matches Str::slug() for ordinary input of up to 255 characters, so adopting the package keeps the URLs you already have.
Race-safe uniqueness
Unique globally, per tenant or per parent — backed by engine-native unique indexes and an automatic retry when two writers collide.
Locale-aware routing
Implicit, scoped and soft-deleted route bindings resolve by slug in the current locale, with an optional primary-key fallback.
SEO slug history
Retired slugs 301-redirect to the current URL without revealing drafts, trashed rows or other tenants’ records.
Policies & locks
Decide how updates, hand-typed slugs and empty sources behave, and freeze a slug once a page is published.
Facade, DI or Artisan
Slugs::model(Product::class) backfills slugs, audits duplicates and builds indexes — or inject SlugManager, run an action or use Artisan.
Documentation
Installation
Install via Composer; publish the config, translations and slug-history migration only when you need them.
Configuration
Every config key with its default and env variable — formatting, uniqueness, update policy, locales, limits, retries and history.
Quick start
Add the HasSlug trait and the Sluggable contract — with zero config, a unique slug is generated from name on create.
Slug definitions
Declare any number of slug columns with slugOptions() or #[Slug] attributes — sources, formatting, uniqueness, lifecycle and routing.
The Slugs facade
One entry point for everything that is not a model hook — flat verbs for one model, and Slugs::model(Product::class) for class-wide work.
DI and actions
Inject SlugManager for the facade’s API without static calls, or run the action behind a method directly with its DTO.
Formatting & suffixes
How the pipeline transliterates, strips and truncates — plus separators, affixes, reserved words, custom sluggers and collision suffixes.
Locale maps
Multi-language slugs in one json/jsonb column — per-locale transliteration, target locales, fallback chains and your own locale source.
Update, manual & lock policies
Control when slugs regenerate, how hand-typed values are treated, what an empty source does, and how to freeze a slug.
Uniqueness & concurrency
Global, scoped or closure-based uniqueness, soft-delete handling, bounded probing and an automatic retry when two writers collide.
Schema & unique indexes
Blueprint macros for slug columns and engine-native unique indexes for string and locale-map slugs on PostgreSQL, MySQL and SQLite.
Querying
Find models by slug — exact locale or the fallback chain, any locale, lists — with scopes that stay safe for count() and paginate().
Route model binding
Implicit, custom-column, scoped and soft-deleted route binding by slug, locale-aware URLs and an optional primary-key fallback.
Slug history & redirects
Keep retired slugs and 301-redirect old URLs to the current one — without revealing drafts, trashed rows or other tenants’ records.
Validation
UniqueSlug and ValidSlug rules with the exact generation semantics — scoped, locale-aware and safe against cross-tenant probing.
Events & exceptions
SlugChanged, SlugCollisionRetried and SlugsRegenerated events, plus the typed exception hierarchy behind every failure.
Artisan commands
Backfill and regenerate slugs, create engine-native indexes, and audit duplicates before adding a unique index.
Adoption & limitations
Map the options of an existing slug setup onto sluggable, compose it with other traits, and know exactly where its limits are.
Testing
Test slug generation, events, history redirects and queued regeneration in your own Pest suite through the public API — no special setup.
Requirements
PHP 8.4+ with ext-json and ext-mbstring, Laravel 12 or 13, and PostgreSQL, MySQL/MariaDB or SQLite for the unique-index DDL.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.