basekit-laravel/basekit-laravel-slugs
Generate clean, unique, SEO-friendly slugs for your Laravel models with ease. BaseKit Slugs handles transliteration, duplicate prevention, and custom separators, integrating seamlessly through Eloquent traits and model events. Lightweight, configurable, and quick to install.
basekit-laravel-slugs is a brand-new package (v1.0.0, first public release), so the fastest path is to read the source before trusting the README — at 1.0.0 the docs are usually thinner than the code.
Minimal steps:
composer require basekit-laravel/basekit-laravel-slugs:^1.0php artisan vendor:publish --provider="BaseKit\Slugs\SlugsServiceProvider"php artisan about or php artisan package:discover), since a missing provider is the usual cause of "class not found" on a fresh 1.0.0 install.slug column exists (add a migration if the package doesn't ship one you're meant to publish).First use case to validate the package end-to-end: a Post model with a title that gets a unique slug on create, then resolved via route model binding (/posts/{post:slug}). If that works, the package is doing its job; everything else is configuration.
Where to look first, in order: src/ for the trait and the slug-generation class, config/ for the source field / separator / uniqueness strategy options, then tests/ — a 1.0.0 package's test suite is the most reliable documentation of intended behaviour.
Trait-on-model, generation on create. The dominant pattern is a HasSlug-style trait plus a model property (or method) naming the attribute the slug is derived from. Set the source field explicitly rather than relying on a default, so a later column rename doesn't silently produce empty slugs.
Explicit slug source and per-model overrides. Most slug packages let you declare protected $slugSource = 'title'; on the model or override a generateSlug() method. Prefer the override when you need business logic in the slug (e.g. prefixing with a category, stripping marketing noise, or truncating at a word boundary).
Uniqueness scoping. If you have multi-tenant data, slugs almost always need to be unique per tenant, not globally. Look for a scope/where hook in the config and wire it to your tenant column early — retrofitting uniqueness scopes after you have data means a backfill plus a schema change.
Route model binding. Once the column exists, bind on it: Route::get('/posts/{post:slug}', ...). Remember this changes the failure mode — a renamed slug turns old URLs into 404s unless you add a redirect layer or a slug-history table.
Backfilling existing rows. Don't expect the trait to help with records created before install. Write a one-off artisan command or queued job that iterates existing models with chunkById() and generates missing slugs, then add a NOT NULL constraint. Run it in batches; generating slugs for a large table in a single request is a common way to hit memory limits.
Renaming behaviour is a product decision, not a technical one. Decide deliberately whether editing a title regenerates the slug. Stable slugs (generate once, never change) are safer for SEO and inbound links; regenerating requires slug history plus redirects. Most packages default to generate-once — verify and document your choice.
API/resource layer. Expose the slug alongside the ID rather than replacing it. Internal references keep using the primary key; only public URLs switch to slugs. This keeps the change small and reversible.
slug. A soft-deleted row still occupies the slug, so the generator's uniqueness check may hand out a "-2" suffix while the original row is invisible to users. Decide whether uniqueness ignores trashed rows (which requires a partial index in Postgres, or an app-level check in MySQL) and make it explicit.VARCHAR(255) or get silently truncated by MySQL's non-strict mode. Use a sensible length and truncate in the generator, keeping the -2, -3 suffix intact after truncation rather than appending past the limit.Str::slug() punts on many scripts, producing empty strings for e.g. fully CJK titles. Add a fallback (random suffix, ID-based slug, or a custom transliterator) and test with your real content, not just English titles."2026" or "!!!" yields a degenerate or empty slug. Guard in the generator and fall back to a UUID fragment.php artisan config:clear in development and ensure the deploy pipeline runs config:cache — a stale cached config is a classic "my setting isn't applied" afternoon.Generator::class key, or model-level override methods. Prefer registering your own implementation in a service provider — v1.0.0 packages change fast and hard forks make upgrades painful.^1.0 with a committed composer.lock, and read the changelog before each minor bump — slug-generation changes can silently rewrite URLs, so pair upgrades with a smoke test that asserts known slugs are unchanged.x, updating the title does/doesn't change it" is cheap insurance against a minor release altering default behaviour.How can I help you explore Laravel packages today?