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

Easyslugger Laravel Package

javiereguiluz/easyslugger

EasySlugger is a lightweight PHP library for generating clean, URL-friendly slugs from any text. It handles UTF-8 and transliterates accented and non-Latin characters, and can produce unique slugs for database records. Simple static API with no framework dependencies.

Deep Wiki
Context7

Getting Started

EasySlugger is a tiny, dependency-free PHP library — not a Laravel package. There is no service provider, no facade, and no auto-discovery. You install it and call static methods.

composer require javiereguiluz/easyslugger

The first thing to understand is that it ships three slugger classes, and picking the wrong one is the most common early mistake:

Class Behavior Use when
EasySlugger\Slugger Basic ASCII-oriented slugging Input is already Latin/ASCII
EasySlugger\Utf8Slugger Full UTF-8 transliteration (á → a, ñ → n, ç → c, etc.) Any user-supplied or multilingual text
EasySlugger\SeoSlugger SEO-tuned variant You want search-engine-oriented output

The API is a single static call with an optional separator:

use EasySlugger\Utf8Slugger;

$slug = Utf8Slugger::slugify('Héllo, Wörld! Año 2024');
// "hello-world-ano-2024"

First use case: generating a slug from a title on your Post model.

$post = Post::create([
    'title' => $request->title,
    'slug'  => Utf8Slugger::slugify($request->title),
]);

Look first at the library's own source (src/) — it is small enough to read end-to-end in one sitting, and doing so will tell you exactly which characters each slugger handles. Do not assume behavior parity with Laravel's Str::slug(); verify.

Implementation Patterns

Encapsulate it in a trait

Because the library is static and stateless, wrap it once so you can swap the implementation later (and so tests can mock it).

trait HasSlug
{
    public static function bootHasSlug(): void
    {
        static::saving(function ($model) {
            if (empty($model->slug)) {
                $model->slug = Utf8Slugger::slugify($model->{$model->slugSource()});
            }
        });
    }

    protected function slugSource(): string
    {
        return 'title';
    }
}

Enforce uniqueness yourself

The library does not check the database. Day-to-day you will want a suffix strategy:

public static function uniqueSlug(string $source, string $column = 'slug'): string
{
    $base = Utf8Slugger::slugify($source);
    $base = $base ?: 'item';              // guard against empty output
    $slug = $base;
    $i = 2;

    while (static::query()->where($column, $slug)->exists()) {
        $slug = "{$base}-{$i}";
        $i++;
    }

    return $slug;
}

For high-concurrency writes, do this inside a DB::transaction() with a unique index on the column and retry on constraint violation — the naive loop races.

Route model binding

Once slugs exist, use them in URLs.

public function getRouteKeyName(): string
{
    return 'slug';
}

Keep the slug column unique() and index(); you will hit it on every route resolution.

Backfill with an Artisan command

For existing records, a one-off command with chunking is the standard pattern:

Post::whereNull('slug')->chunkById(500, function ($posts) {
    foreach ($posts as $post) {
        $post->update(['slug' => Post::uniqueSlug($post->title)]);
    }
});

Bulk importers

This is where EasySlugger shines — it is genuinely fast in tight loops. For imports, generate all slugs in memory first, then upsert, rather than saving row-by-row and running the uniqueness query each time.

Integration tip: prepare in FormRequests

If you want deterministic slugs from a normalized source, do the slugification in prepareForValidation() or a DTO, and treat the model as a plain consumer. That keeps model events dumb and testable.

Gotchas and Tips

  • It is unmaintained. Last release was 2015-04-12. Expect no fixes. Pin the version and be prepared to replace it.
  • License is flagged NOASSERTION. Before shipping to production, confirm the actual license text in the repository (historically MIT) with your legal/compliance process.
  • PHP 8 compatibility is not guaranteed. The 2015 code predates PHP 7 type declarations and PHP 8 strictness. Run it under your target PHP version early; watch for deprecation notices (particularly around utf8_* helpers if used internally).
  • No Laravel integration at all. If you want a facade or DI, write a small wrapper service and register it — do not expect EasySlugger to appear magically.
  • Laravel already has Str::slug(). Since Laravel 6+, Str::slug() uses voku/portable-ascii and handles UTF-8 well. For new projects, benchmark against Str::slug() before adding a dependency. EasySlugger's argument is raw speed and a small footprint, not capability.
  • Wrong class, wrong output. Slugger on accented input produces very different (often stripped) results than Utf8Slugger. Always test with the exact input language of your users.
  • Empty slugs happen. Strings that are entirely punctuation, emoji, or non-transliterable scripts can yield ''. Always coalesce to a fallback.
  • Separator is passed as a string — stick to a single character (-, _) to avoid surprises with repeated-consecutive handling.
  • No stop-word removal in the basic sluggers, so long titles produce long slugs. Add a length cap (Str::limit) or a custom truncation if URL aesthetics matter.
  • Case is lowercased — if you need to preserve case, this library is the wrong tool.
  • Testing: assert exact slugs for your real-world inputs (accents, CJK, punctuation, numbers) rather than trusting the README examples. This doubles as your migration test when (not if) you swap the library out.
  • Extension point: the classes are simple enough to extend or copy. If you need custom transliteration rules, fork the class into your app/Support namespace rather than patching vendor code.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core