NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

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

KeyDefaultEnvPurpose
defaults.columnslug—Column of the zero-config definition.
defaults.sourcename—Source attribute of the zero-config definition.
defaults.separator-—1–3 characters from - . _ ~
defaults.max_length255—Characters incl. prefix/suffix and collision suffix (8–2048).
defaults.max_wordsnull—Word cap (null or blank = none).
defaults.languageenSLUGGABLE_LANGUAGETransliteration language of string slugs (locale maps use each locale).
defaults.dictionary['@' => 'at']—Replacements applied before stripping.
defaults.lowercasetrue—Lowercase the slug.
defaults.unicodefalse—Keep non-ASCII letters instead of transliterating.
defaults.uniquenessglobal—none / global / scoped
defaults.locale_uniquenessper_locale—per_locale / across_locales
defaults.include_trashedtrue—Soft-deleted rows keep their slug reserved.
defaults.on_createtrue—Generate on create.
defaults.on_updateif_emptySLUGGABLE_ON_UPDATEnever / if_empty / when_source_changes / always
defaults.manualnormalize—normalize / verbatim / strict
defaults.empty_sourcerandom—random / skip / fail
defaults.suffixsequential—sequential / random (custom needs suffixUsing()).
defaults.suffix_start2—First sequential suffix (1–1000).
defaults.random_length8—Random slug / random suffix length (4–32).
defaults.target_localessource—source / supported / current
defaults.locale_fallbackany—none / fallback / any — the reading and binding chain.
reserved[]—Slugs nobody may take (case-insensitive).
locales.supportednull—Locale list; null = app.locale + app.fallback_locale.
locales.fallbacknullSLUGGABLE_FALLBACK_LOCALEnull or blank = app.fallback_locale.
binding.key_fallbackfalse—Try the primary key after a slug miss.
limits.max_source_length2000—Source characters considered (64–100000).
limits.sequential_probes50—Sequential candidates before random ones (1–1000).
limits.probe_batch10—Candidates checked per query (1–100).
limits.random_attempts10—Random candidates before giving up (1–100).
concurrency.retries3SLUGGABLE_RETRIESRetries after a unique-index race (0–20; 0 disables).
history.enabledfalseSLUGGABLE_HISTORYDefault for keepHistory().
history.redirecttrueSLUGGABLE_HISTORY_REDIRECTDefault for redirectFromHistory().
history.redirect_status301—301 / 302 / 307 / 308
history.avoid_reusefalse—Default for avoidHistoricalSlugs().
history.tableslug_historySLUGGABLE_HISTORY_TABLEHistory table.
history.modelSlugHistory::class—Swappable history model (must extend it; anything else throws).
history.prune_after_daysnullSLUGGABLE_HISTORY_PRUNE_DAYSPrune age (1–36500); null or blank (SLUGGABLE_HISTORY_PRUNE_DAYS=) keeps forever.
key_typebigintSLUGGABLE_KEY_TYPEPrimary-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=bigint

Per-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 crypto

By 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.