lemaur/laravel-human-id
Laravel Human ID generates short, human-friendly unique identifiers for Eloquent models, making URLs, references, and public-facing records easier to read and share while keeping database keys stable.
lemaur/laravel-human-id generates memorable, human-readable primary keys (e.g. brave-otter-42) instead of auto-increment integers — useful for public-facing URLs, support references, and anywhere an opaque numeric ID leaks volume or looks unfriendly.
Minimal setup:
composer require lemaur/laravel-human-id
php artisan vendor:publish --tag="human-id-config"
Then make the model's key a string and opt in via the trait:
// migration
$table->string('id')->primary();
// app/Models/Post.php
use LeMaur\HumanId\Traits\HasHumanId;
class Post extends Model
{
use HasHumanId;
}
Where to look first:
config/human-id.php — separator, word/segment count, and the adjective/noun word lists you'll want to customise per domain.HasHumanId trait — it wires generation into the creating event and flips $incrementing/$keyType for you.HumanId facade — for generating IDs outside of Eloquent (imports, external references, batch jobs).First use case: replace Route::get('/posts/{post}') integer binding with the model's own human key. No extra route-key configuration is needed because the human ID is the primary key.
Model trait (the default path). Add HasHumanId to any model whose key should be human-readable. On creating, the trait fills the id attribute before insert, so Post::create([...]) just works and the model is immediately routable:
$post = Post::create(['title' => 'Hello']);
$post->id; // "calm-fox-42"
route('posts.show', $post); // /posts/calm-fox-42
Explicit generation via the facade. For non-Eloquent contexts — invoice numbers, job correlation IDs, seeded fixtures — call the generator directly and pass uniqueness context if you need collision checking against a table:
use LeMaur\HumanId\Facades\HumanId;
$reference = HumanId::generate();
Per-model configuration. Rather than one global word list, define domain-specific vocabularies in config (e.g. animals for users, colours for tags) and select the relevant set per model. This keeps IDs on-brand and makes them guessable-by-category, which is often desirable for support workflows.
Route keys and lookups. Because the key is the primary key, find(), findOrFail(), route model binding, and whereKey() all behave normally. Keep controllers on findOrFail($humanId) rather than string-matching slugs — you get 404 semantics for free.
Integration points to wire up deliberately:
id as-is; it is already URL-safe.string of matching length, not unsignedBigInteger.id explicitly for deterministic test fixtures.$incrementing = false and $keyType = 'string'. If the trait is added but the migration still declares $table->id(), inserts fail. Change both together, and update every foreign key that points at the table.id out of $fillable. Mass-assignment of id will bypass generation (or produce inconsistent behaviour). Generate, don't accept, the key from request input.Calm-Fox and calm-fox collide at the DB level even if the generator treats them as distinct. Normalise casing in config or use a binary/case-sensitive collation.config/human-id.php, run php artisan config:clear locally and remember config:cache in deploy scripts — stale cached word lists are a classic "why is it still generating old words" moment.creating, so retried jobs produce a fresh value per attempt; if downstream systems depend on the ID, persist the model before dispatching or use ShouldBeUnique semantics carefully.human-id.php gained new keys, and re-run the suite after composer update since trait internals around the creating hook are the most likely place for subtle behaviour shifts.How can I help you explore Laravel packages today?