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

Per-locale slugs

Slugs are owned by sluggable-for-laravel, installed with this package. A translatable model gets per-locale slugs with two traits and two interfaces — no cast, no glue:

use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Sluggable\Concerns\HasSlug;
use RoundlyConsulting\Sluggable\Contracts\Sluggable;
use RoundlyConsulting\Sluggable\Definitions\SlugDefinition;
use RoundlyConsulting\Sluggable\Definitions\SlugOptions;
use RoundlyConsulting\Translatable\Concerns\HasTranslations;
use RoundlyConsulting\Translatable\Contracts\Translatable;

final class Topic extends Model implements Translatable, Sluggable
{
    use HasTranslations;
    use HasSlug;

    /** @var list<string> */
    public array $translatable = ['name', 'description', 'slug'];

    public function slugOptions(): SlugOptions
    {
        return SlugOptions::make(
            SlugDefinition::for('slug')->from('name')->routeKey(),
        );
    }

    /** @return list<string> */
    protected function translationStatusExcludes(): array
    {
        return ['slug'];
    }
}

$topic = Topic::create(['name' => ['en' => 'Investing', 'sk' => 'Investovanie']]);
$topic->getTranslations('slug');   // ['en' => 'investing', 'sk' => 'investovanie']

How it fits together

  • Translatable extends sluggable’s ProvidesLocaleMaps, and HasTranslations implements it: isLocaleMapAttribute() → isTranslatableAttribute(), getLocaleMap() → getTranslations(), setLocaleMap() → setTranslations().
  • Sluggable therefore detects the slug attribute as a locale map and builds the sk slug from the sk name regardless of the request locale.
  • Every slug it writes goes through translatable’s locale-key validation and strict_locales.
  • The model must implement Translatable. A model using HasTranslations without the interface has no cast on its slug, so sluggable refuses it with InvalidSlugDefinitionException (localeMapContractMissing) rather than treating the jsonb column as a plain string.
  • Slugs are generated for exactly the supported locales, through the TranslatableSlugLocales binding (see Configuration).

Uniqueness, engine-native unique indexes (SlugIndexes, php artisan sluggable:indexes), querying (whereSlug, whereSlugInAnyLocale, findBySlug), route binding, the UniqueSlug validation rule, slug history and redirects all belong to sluggable — see its documentation.

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.