marquine/eloquent-uuid
marquine/eloquent-uuid adds UUID primary key support to Laravel Eloquent models, replacing auto-incrementing integer IDs with globally unique identifiers through a simple trait. Models keep their usual relationships and query behavior while gaining non-sequential, harder-to-guess keys with minimal c...
Minimal steps
composer require marquine/eloquent-uuidcomposer.json in vendor/marquine/eloquent-uuid — check the illuminate/* and php constraints against your app. With 7 stars and 0 dependents, this package was likely written for Laravel 5.x/6.x; confirm it installs at all on Laravel 10/11/12.src/ — identify the trait's exact FQCN (typically something like Marquine\EloquentUuid\HasUuid) and read its boot…() method to see which model event generates the UUID.README.md — the canonical usage snippet.use Marquine\EloquentUuid\HasUuid; // verify FQCN in vendor/marquine/eloquent-uuid/src
class Order extends Model
{
use HasUuid;
}
bigIncrements):Schema::create('orders', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('user_id')->index();
$table->timestamps();
});
Where to look first: the trait source, not the README. You need to know whether it hooks creating, sets $keyType/$incrementing for you, and whether it can generate a custom version/namespace UUID.
First use case: a greenfield model where you want non-guessable, client-generatable IDs — e.g. an Order or Invoice whose ID appears in URLs, outbound emails, or is created offline before sync.
Reality check before you start: Laravel 9+ ships Illuminate\Database\Eloquent\Concerns\HasUuids (and HasUlids). For any modern app this package is almost certainly redundant — the correct "getting started" step is often to use the framework trait instead and skip the dependency entirely.
Consistent trait application. Apply it to every model that owns a UUID PK, and be explicit in the model rather than relying on a base class you might forget:
abstract class UuidModel extends Model
{
use HasUuid;
}
But prefer per-model traits — a base class that silently flips $incrementing is a common source of confusion for the next developer.
Migrations and foreign keys. Every referencing column must be uuid, and the parent must be created first:
$table->foreignUuid('order_id')->constrained()->cascadeOnDelete();
// or pre-Laravel 7 syntax: $table->uuid('order_id'); $table->foreign('order_id')->references('id')->on('orders');
Mixing foreignId (bigint) with a UUID PK is the single most common integration failure.
Route model binding just works. Because the key is a string, Route::get('/orders/{order}', ...) resolves via getRouteKey() with no changes. You get 404s automatically for malformed/unknown IDs — no where('id', '[0-9]+') constraint needed, which is a nice win.
Factories. Don't hard-code IDs; let the trait generate them. If you need deterministic IDs in tests, override explicitly:
Order::factory()->create(['id' => Str::uuid()]);
Relationships. hasMany/belongsTo need no special handling, but set protected $keyType = 'string'; on any related model if you also query it by key outside Eloquent's generated SQL.
Bulk operations are the trap. Model events drive UUID generation, so Order::insert([...]), DB::table('orders')->insert(), upsert(), and insertGetId() will produce NULL or failing primary keys. For imports, build models and save() them, or generate IDs yourself with Str::uuid() in the array.
API/JSON layer. UUID PKs serialize as strings — fine for JSON — but if you have clients expecting integers, this is a breaking contract change. Treat the migration as a v2 API concern, not a refactor.
Drop-in alternative pattern. If you're on Laravel 9+, the equivalent native implementation is:
use Illuminate\Database\Eloquent\Concerns\HasUuids;
class Order extends Model { use HasUuids; }
Same migration shape, no third-party dependency, framework-maintained. Migration from this package is usually a one-line trait swap.
$incrementing and $keyType. The trait may or may not set these. Verify with $model->getIncrementing() and $model->getKeyType() in php artisan tinker. If getIncrementing() is true, inserts will try to treat your UUID as an auto-increment integer and fail confusingly.Model::insert() skips creating. If you see SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'id' cannot be null, that's the cause — not a trait misconfiguration.ORDER BY id is meaningless and index locality is poor on insert-heavy tables. Use created_at for ordering, or switch to ULIDs/UUIDv7 if sortable keys matter.char(36) PKs bloat indexes. For high-volume tables, consider binary(16) with a cast — but that is well beyond what this package offers, so don't expect help there.unique/primary so the DB enforces it.boot{TraitName}. If you alias or wrap the trait in your own class, the boot hook may not fire — the symptom is NULL IDs with no error until the DB rejects the insert. Debug by dumping $model->getKey() in a creating listener.ramsey/uuid, wildbit/laravel-uuid, spatie/laravel-uuid, and Laravel's native traits can all collide on $keyType, casts, or boot hooks. Use exactly one.generateUuid()-style method (check the source), override it in your model to control version/namespace or to use ULIDs. That's the cleanest way to adapt the package without forking.composer require resolves on your PHP/Laravel pair. On anything Laravel 9+, prefer the built-in HasUuids; on older versions, ramsey/uuid inside a creating hook is a five-line, dependency-light alternative you fully control.How can I help you explore Laravel packages today?