Configuration
Every key has a working default, so most apps never publish the config. The published config/sluggable.php in full:
return [
'defaults' => [
'column' => 'slug',
'source' => 'name',
'separator' => '-',
'max_length' => 255,
'max_words' => null,
'language' => env('SLUGGABLE_LANGUAGE', 'en'),
'dictionary' => ['@' => 'at'],
'lowercase' => true,
'unicode' => false,
'uniqueness' => 'global',
'locale_uniqueness' => 'per_locale',
'include_trashed' => true,
'on_create' => true,
'on_update' => env('SLUGGABLE_ON_UPDATE', 'if_empty'),
'manual' => 'normalize',
'empty_source' => 'random',
'suffix' => 'sequential',
'suffix_start' => 2,
'random_length' => 8,
'target_locales' => 'source',
'locale_fallback' => 'any',
],
'reserved' => [],
'locales' => [
'supported' => null,
'fallback' => env('SLUGGABLE_FALLBACK_LOCALE'),
],
'binding' => [
'key_fallback' => false,
],
'limits' => [
'max_source_length' => 2000,
'sequential_probes' => 50,
'probe_batch' => 10,
'random_attempts' => 10,
],
'concurrency' => [
'retries' => env('SLUGGABLE_RETRIES', 3),
],
'history' => [
'enabled' => env('SLUGGABLE_HISTORY', false),
'redirect' => env('SLUGGABLE_HISTORY_REDIRECT', true),
'redirect_status' => 301,
'avoid_reuse' => false,
'table' => env('SLUGGABLE_HISTORY_TABLE', 'slug_history'),
'model' => \RoundlyConsulting\Sluggable\Models\SlugHistory::class,
'prune_after_days' => env('SLUGGABLE_HISTORY_PRUNE_DAYS'),
],
'key_type' => env('SLUGGABLE_KEY_TYPE', 'bigint'),
];Every key
| Key | Default | Env | Purpose |
|---|---|---|---|
defaults.column | slug | — | Column of the zero-config definition. |
defaults.source | name | — | Source attribute of the zero-config definition. |
defaults.separator | - | — | 1–3 characters from - . _ ~ |
defaults.max_length | 255 | — | Characters incl. prefix/suffix and collision suffix (8–2048). |
defaults.max_words | null | — | Word cap (null or blank = none). |
defaults.language | en | SLUGGABLE_LANGUAGE | Transliteration language of string slugs (locale maps use each locale). |
defaults.dictionary | ['@' => 'at'] | — | Replacements applied before stripping. |
defaults.lowercase | true | — | Lowercase the slug. |
defaults.unicode | false | — | Keep non-ASCII letters instead of transliterating. |
defaults.uniqueness | global | — | none / global / scoped |
defaults.locale_uniqueness | per_locale | — | per_locale / across_locales |
defaults.include_trashed | true | — | Soft-deleted rows keep their slug reserved. |
defaults.on_create | true | — | Generate on create. |
defaults.on_update | if_empty | SLUGGABLE_ON_UPDATE | never / if_empty / when_source_changes / always |
defaults.manual | normalize | — | normalize / verbatim / strict |
defaults.empty_source | random | — | random / skip / fail |
defaults.suffix | sequential | — | sequential / random (custom needs suffixUsing()). |
defaults.suffix_start | 2 | — | First sequential suffix (1–1000). |
defaults.random_length | 8 | — | Random slug / random suffix length (4–32). |
defaults.target_locales | source | — | source / supported / current |
defaults.locale_fallback | any | — | none / fallback / any — the reading and binding chain. |
reserved | [] | — | Slugs nobody may take (case-insensitive). |
locales.supported | null | — | Locale list; null = app.locale + app.fallback_locale. |
locales.fallback | null | SLUGGABLE_FALLBACK_LOCALE | null or blank = app.fallback_locale. |
binding.key_fallback | false | — | Try the primary key after a slug miss. |
limits.max_source_length | 2000 | — | Source characters considered (64–100000). |
limits.sequential_probes | 50 | — | Sequential candidates before random ones (1–1000). |
limits.probe_batch | 10 | — | Candidates checked per query (1–100). |
limits.random_attempts | 10 | — | Random candidates before giving up (1–100). |
concurrency.retries | 3 | SLUGGABLE_RETRIES | Retries after a unique-index race (0–20; 0 disables). |
history.enabled | false | SLUGGABLE_HISTORY | Default for keepHistory(). |
history.redirect | true | SLUGGABLE_HISTORY_REDIRECT | Default for redirectFromHistory(). |
history.redirect_status | 301 | — | 301 / 302 / 307 / 308 |
history.avoid_reuse | false | — | Default for avoidHistoricalSlugs(). |
history.table | slug_history | SLUGGABLE_HISTORY_TABLE | History table. |
history.model | SlugHistory::class | — | Swappable history model (must extend it; anything else throws). |
history.prune_after_days | null | SLUGGABLE_HISTORY_PRUNE_DAYS | Prune age (1–36500); null or blank (SLUGGABLE_HISTORY_PRUNE_DAYS=) keeps forever. |
key_type | bigint | SLUGGABLE_KEY_TYPE | Primary-key type of the slugged models (bigint / uuid / ulid, case-insensitive); anything else throws InvalidConfigurationException. |
Every key is validated on first use: a typo or an out-of-range value (say SLUGGABLE_HISTORY=disabled) throws InvalidSlugDefinitionException naming the key instead of silently falling back. Switches accept true/false, 1/0, on/off and yes/no; the string keys (column, source, separator, language, history.table, locales.fallback) must be strings; reserved and locales.supported must be lists of non-empty strings and dictionary a string ⇒ string map — a bad entry throws rather than being dropped. Only a key that is not set — absent, null or blank (a host’s KEY=) — takes its default. An unrecognised key_type throws the toolkit’s InvalidConfigurationException.
Environment
Nine keys are env-backed — shown here with example values:
SLUGGABLE_LANGUAGE=en
SLUGGABLE_FALLBACK_LOCALE=en
SLUGGABLE_ON_UPDATE=when_source_changes
SLUGGABLE_RETRIES=3
SLUGGABLE_HISTORY=true
SLUGGABLE_HISTORY_REDIRECT=true
SLUGGABLE_HISTORY_TABLE=slug_history
SLUGGABLE_HISTORY_PRUNE_DAYS=365
SLUGGABLE_KEY_TYPE=bigintPer-definition overrides
The defaults block only fills options a slug definition leaves unset. Anything you set on a SlugDefinition or a #[Slug] attribute wins for that column.
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.