Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Urlify Laravel Package

jbroadway/urlify

A PHP slug generator that transliterates Unicode text to ASCII and creates clean, SEO-friendly URLs. Supports multiple languages, custom character mappings, and Laravel integration for easy slug creation.

Deep Wiki
Context7

Getting Started

Install

composer require jbroadway/urlify

There is nothing to publish, no service provider, no config file, and no facade. It is a single static class autoloaded via Composer. Just import and call:

use jbroadway\URLify;

echo URLify::filter('Hello, Wörld!');          // hello-world
echo URLify::filter('Hello, Wörld!', 200, 'de'); // hello-woerld

That is the entire "getting started". The class is jbroadway\URLify.

Where to look first

  • src/URLify.php — the whole API (filter, downcode, addChars, removeWords).
  • src/URLify/maps/ — per-language transliteration tables (one file per locale). This is where the de → ue, tr → i, etc. behavior comes from.
  • If a character is not being converted as expected, grep the map files before opening an issue — the fix is usually adding a mapping, not changing code.

First use case

Slugging a model title on create. The most direct integration is a saving hook:

protected static function booted(): void
{
    static::saving(function (self $model) {
        $model->slug ??= URLify::filter($model->title);
    });
}

That is the 90% case: filter() returns a lowercase ASCII string with - separators, safe for URLs, ready for a unique column.

Implementation Patterns

Wrap it — don't scatter static calls. A small App\Support\Slug helper or an injectable Slugger service gives you one place to configure language, max length, and stop words, and it keeps URLify calls out of Blade templates and closures:

namespace App\Support;

use jbroadway\URLify;

class Slug
{
    public static function make(string $value, ?string $language = null, int $max = 200): string
    {
        return URLify::filter($value, $max, $language ?? config('slugs.language', ''));
    }
}

Locale-aware slugs. Pass the $language argument to get correct transliteration rules. de maps ü → ue and ß → ss; tr handles the dotted/dotless i; da/no handle æ, ø, å. If your app is multi-locale, resolve the language from app()->getLocale() (or a per-model slugLocale property) rather than hard-coding it, so slugs stay stable when editors switch UI language.

Slug uniqueness belongs to you. filter() does not check for collisions and does not append suffixes. Implement that explicitly:

$base = Slug::make($model->title);
$slug = $base;
$i = 2;
while (static::where('slug', $slug)->exists()) {
    $slug = "{$base}-{$i}";
    $i++;
}
$model->slug = $slug;

Put this in a dedicated action or observer so it also runs during imports and seeding.

Use downcode() for search, not slugs. URLify::downcode($text) strips accents and non-ASCII but preserves casing, spaces, and punctuation. That is the right tool for normalizing a searchable column or a "name" index, while filter() is the right tool for a URL segment.

Custom character maps at boot. If your domain has recurring symbols (currency, ligatures, brand marks), register them once:

// config/slugs.php returns char => replacement
URLify::addChars(config('slugs.custom_chars', []));

Do this in a service provider's boot(), not inside a request handler, so the mappings apply consistently everywhere (queues, seeders, CLI import commands).

Stop-word removal for cleaner slugs. For content sites where titles are conversational ("The Best Of The Best"), URLify::removeWords(['the', 'a', 'an', 'of']) produces shorter, prettier URLs. Register it alongside addChars in the provider.

Integration points that work well:

  • Model observers — saving for generation, updating if the slug should track title changes (usually with a "keep existing on update" guard).
  • Form Requests — validate the submitted slug with alpha_dash and Rule::unique(...)->ignore($model), and normalize user-supplied slugs through URLify::filter() before saving.
  • Import commands / queue jobs — the static class is safe to call from anywhere, no container bootstrapping needed.
  • Blade — don't. Generate the slug in the model or a controller action; keep templates free of transliteration logic.

Gotchas and Tips

Static mutable state and Octane. addChars() and removeWords() mutate class-level static arrays for the life of the process. Under Octane (or any long-running worker), calling them inside a controller will leak between requests. Register them once in a provider, or make the calls idempotent and guard them with a booted flag.

The language argument must match a map file. Passing 'pt-BR' when only pt exists silently falls back to the default (English) map — you get no error, just wrong transliteration for ç/ã. Use the short code, and verify against the file names in src/URLify/maps/.

maxLength truncates, it does not word-boundary. The default 200-character cap cuts the string at the boundary and trims the trailing separator, which can leave a truncated word (...-translatio). If that matters, either validate length upstream or slice on your own word boundary before calling filter().

Uniqueness, casing, and emptiness. filter() returns lowercase and may return an empty string for inputs that are entirely non-transliterable (emoji, CJK-only titles without a language map that handles them). Always fall back:

$slug = Slug::make($title) ?: Str::random(8);

It is opinionated about what survives. Punctuation, symbols, and HTML-ish content are stripped/normalized aggressively. If your titles contain meaningful markup, sanitize first and confirm the output on a sample of real records rather than assuming.

mbstring is required. The library relies on mb_* functions for UTF-8 handling. It's on by default in most modern Laravel installs, but it is a real requirement in minimal Docker images — add ext-mbstring to your composer.json require block.

Comparison to Str::slug(). Laravel's built-in is fine for Latin-only projects and needs no dependency. URLify wins when you need (a) language-specific transliteration (German umlauts, Turkish dotted i, Nordic letters), (b) configurable stop-word removal, or (c) consistent output across many locales. If neither (a) nor (b) applies, Str::slug() is one fewer dependency.

Extension points are the maps, not the code. For most "wrong output" bugs, the fix is addChars() or a project-local map, not a fork. Keep those overrides in config/slugs.php so upgrades to the package do not clobber them.

Test the actual strings. Because the behavior is data-driven from the map files, unit-test representative inputs (your real product titles, your worst emoji case, your longest title) rather than trusting the docs. A SlugTest with a handful of assertSame cases catches map regressions on package updates quickly.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource