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

Orm Slug Field Laravel Package

mindy/orm-slug-field

A Laravel/PHP ORM field package that adds automatic URL-friendly slug generation and management to your models, with configurable source fields and uniqueness handling for clean, SEO-friendly routes.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require mindy/orm-slug-field

This package has 1 star and 0 dependents, so treat it as a thin companion to MindyPHP's ORM rather than a battle-tested library. Read the source in vendor/mindy/orm-slug-field/src/ first — it is small enough to audit in a few minutes before you wire it into a model.

Where to look first

  1. vendor/mindy/orm-slug-field/src/SlugField.php (or equivalent) — confirm the constructor options and the exact source-field option name (source, sourceField, from, etc. vary between versions).
  2. MindyPHP's CharField / SlugField base class — SlugField almost certainly extends a string field, so all the standard maxLength, null, default, and validation rules apply.
  3. Any test files shipped in the repo — they are the most reliable documentation for a package this small.

Minimal first use case

A blog post whose URL slug is derived from its title:

use Mindy\Orm\Model;
use Mindy\Orm\Fields\CharField;
use Mindy\Orm\Fields\AutoField;
use Mindy\Orm\Fields\SlugField;

class Post extends Model
{
    public static function getFields()
    {
        return [
            'id'    => ['class' => AutoField::class],
            'title' => ['class' => CharField::class],
            'slug'  => [
                'class'  => SlugField::class,
                'source' => 'title',   // verify option name in source
            ],
        ];
    }
}

Save a Post with title = 'Hello World!' and, on save, the model should populate slug = 'hello-world'. That is the entire point of the package: turning a free-text source field into a URL-safe, deduplicated identifier during the ORM write.

Make sure the column exists in your migration (slug VARCHAR(255) UNIQUE is the usual target), then hit it from a controller:

$post = Post::objects()->filter(['slug' => $request->get('slug')])->get();

Implementation Patterns

Pattern: automatic vs. editable slugs. The common production setup is auto-generate on create, respect on update. On INSERT, if slug is empty, derive it from source. On UPDATE, if the user changed the slug manually, leave it alone; only regenerate when the source changed and the slug was untouched. Implement this in an beforeSave hook or by overriding save() on the model — do not rely on the field doing it for you across all versions.

Pattern: scoped slugs for nested resources. Two Posts in different categories can legitimately share hello-world. Store the slug alongside the parent key and enforce uniqueness in a composite index; look up with filter(['slug' => $slug, 'category_id' => $id]).

Pattern: multi-language models. Keep the source and slug on the same translated row, and let the source field (e.g. title_en, title_ru) drive the slug per locale. This is simpler than a shared slug across languages and avoids ambiguity in routing.

Pattern: routing integration. Wire the slug directly into MindyPHP's URL rules, e.g. 'post/<slug:\w+-\w+>' => 'Post/view'. Because slugs are stable per row, treat them as part of your public contract — regenerating them silently breaks links.

Pattern: form handling. Expose the slug as an optional form field with a unique validator mirroring the DB constraint. When empty, show a JS-prefilled value from the title on the client side so the user sees what will be generated before they save.

Pattern: migration + backfill. For an existing table, add the column as nullable, run a one-off script that loads every row, sets slug = SlugField::generate($row->title), dedupes the batch, then add the UNIQUE index and NOT NULL constraint in a follow-up migration.


Gotchas and Tips

Verify the option names before trusting them. At 1 star with no dependents, the API is not stabilised. source, sourceField, from, attribute, and slugFrom are all plausible and could change between patch versions. Pin the version in composer.json and read SlugField.php before assuming.

Uniqueness is the hardest part. Most slug fields handle the naive -2, -3 suffix case, but they almost never do it atomically. Under concurrent inserts you can produce a duplicate and get a DB-level UNIQUE violation. Wrap the save in a small retry loop, or serialise generation with a row lock on the parent record.

Transliteration is locale-dependent. Non-Latin titles (Привет, 日本語, emoji) rely on whatever transliterator is available — iconv, transliterator_transliterate (intl), or a hand-rolled table. Check what the package uses and confirm ext-intl is installed in your production image; otherwise you may silently get empty or garbled slugs.

Empty or purely-symbolic source. title = '...' or title = '!!!' can produce an empty slug, which then collides with itself on every row. Decide on a fallback (uniqid, timestamp, or a hard failure with a clear validation error) and enforce it in the model, not the field.

Length and truncation. A VARCHAR(255) column will reject or truncate long titles. Truncate at a word boundary before saving, not after, so you do not split a multi-byte character — use mb_substr on the generated slug.

Regeneration rules. Do not regenerate on every save. A user editing a typo in the body should not change the URL. Only regenerate when the source field is dirty and the slug has not been manually overridden — track the override in a boolean column or by comparing the current slug against the last auto-generated one.

Reserved words and collisions with routes. admin, api, login, new are common slugs that clash with real routes. Maintain a reserved-word list and apply the same -2 suffix logic to it.

Case sensitivity on MySQL. utf8_general_ci treats Hello and hello as equal — good for slug uniqueness, but it means you must lowercase consistently for lookups or risk confusion across DB engines.

Debugging tips. If slugs are not generated: (1) check the field is actually in getFields(), (2) check source points at a field that is set before save, (3) log inside beforeSave to confirm the hook fires, (4) confirm the column is not NOT NULL without a default — the insert may be silently rejected.

Extension point. If the built-in generator does not fit (e.g. you want SEO-friendly transliteration with a custom mapping), copy the field class into your own namespace and override the slug-generation method. With 1 star, there is no meaningful upstream to depend on — vendoring the ~100 lines is safer than waiting for fixes.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log