amdadulhaq/bangla-slug-laravel
A Laravel package for generating clean, URL-friendly slugs from Bangla text. It converts Bengali strings into readable slugs for permalinks, routes, and Eloquent models, making Bangla content easier to use and share in Laravel applications.
What it is: a thin Laravel wrapper around a Bangla (Bengali) slug generator. Str::slug() strips Bengali script down to an empty string, so this package exists to turn আমার সোনার বাংলা into a usable, URL-safe slug (either romanized to ASCII or preserved as percent-encoded Unicode, depending on config).
Minimal steps
composer require amdad121/bangla-slug-laravelvendor:publish --tag=bangla-slug-config) and read it before writing any code. Two knobs decide everything downstream: transliterate to ASCII vs. keep Unicode, and separator/lowercase.// confirm output shape for your real data
app(\Amdad121\BanglaSlug\BanglaSlug::class)->generate('আমার সোনার বাংলা');
Where to look first
Str::macro is registered.config/bangla-slug.php — the transliteration/ASCII switch and separator.src/ service provider — tells you what is bound in the container and therefore what you can swap.First real use case: a Post model with a Bengali title and a slug column — generate on save, fall back to the record id when the title has no transliterable characters.
Generate in a saving hook, and only when empty. Never regenerate on every update — inbound links and sitemaps break silently.
protected static function booted(): void
{
static::saving(function (self $post) {
if (blank($post->slug)) {
$post->slug = static::uniqueSlug($post->title, $post->id);
}
});
}
Uniqueness with suffixes, plus a unique index. Do the suffixing in PHP and keep a DB unique index as the real guarantee:
protected static function uniqueSlug(string $title, $ignoreId = null): string
{
$base = Str::limit(app(BanglaSlug::class)->generate($title), 180, '');
$slug = $base ?: 'post-'.Str::random(6);
$i = 1;
while (static::where('slug', $slug)->when($ignoreId, fn ($q) => $q->whereKeyNot($ignoreId))->exists()) {
$slug = $base.'-'.(++$i);
}
return $slug;
}
Wrap the insert in a retry on QueryException (SQLSTATE 23000) — two concurrent requests can both pass the exists() check.
Route model binding: public function getRouteKeyName() { return 'slug'; }. If you keep Unicode slugs, verify the router round-trips percent-encoded segments and that your slug column collation matches (see Gotchas).
Backfill as an artisan command, not a migration. chunkById(500) over existing rows, skip non-empty slugs, log the ones that came out empty so you can hand-fix them.
Bilingual content: store both. slug (ASCII, shareable, safe in any client) and slug_bn (Unicode) if you need canonical Bengali URLs. Route to one, canonical-link the other.
Admin panels (Filament/Nova): treat the slug field as read-only-after-create, with an explicit "regenerate" action behind a confirmation — that surfaces the link-breaking risk to editors instead of hiding it in a save hook.
Validation: Rule::unique('posts', 'slug')->ignore($post) in the Form Request, so manual overrides fail loudly rather than throwing at the DB.
U+200D/U+200C) that creep in from copy-paste. Normalize (NFC) and strip joiners before slugging, or you get duplicate-looking slugs that compare unequal.''. Always have a fallback (post-{random} or the id) — a unique index on '' will bite you on the second row.ক্ষ may become kkh, kh, or khy depending on the table. Don't assert exact strings in tests against real-world titles; assert properties (non-empty, ASCII-only, no spaces, stable across calls).$post->regenerating_slug flag or an explicit action. This is the single most common way this package gets misused.utf8mb4 caps indexed columns at 191. Suffixing a 200-char base overflows._ci collation treats Post and post as duplicates; Postgres does not. If you lowercase in config, be consistent — don't rely on the DB to do it.php artisan config:cache after any config edit, especially in Docker/Octane where a stale cached config silently keeps the old separator or ASCII setting.composer update safe.^1.0 and skim the diff only if you've vendored or subclassed internals.assertDatabaseHas on a Bengali title is a 5-line regression test that catches nearly every config drift; add it before you tune anything.How can I help you explore Laravel packages today?