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...
Architecture fit. The package is a narrow, single-responsibility Eloquent trait: it flips a model to a string primary key ($incrementing = false, $keyType = 'string') and generates a UUID on creating. That is an idiomatic Laravel extension point and imposes no framework-level coupling, no service provider gymnastics, and no runtime dependencies beyond illuminate/* and a UUID generator. Architecturally it slots in cleanly where it is needed — but the addressable need is shrinking. Laravel 9+ ships HasUuids in core, and Laravel 11+ generates time-ordered UUIDv7, which is materially better for database index locality than the v4 UUIDs a small 2018-era trait almost certainly produces. The realistic fit is therefore a legacy estate: Laravel 5.5–8 applications, or codebases with an existing UUID convention the team wants to standardize rather than rewrite.
Integration feasibility. Mechanically trivial: one composer require, one use statement on the model, done. The real feasibility question is the composer.json constraint block. A package with 7 stars and 0 dependents is very likely pinned to illuminate/support: 5.x|6.x and will refuse to install on Laravel 10/11/12 — meaning the "integration" degrades into "fork and patch." Budget a half-day spike to run composer require against the actual target stack before committing to any plan.
Technical risk. This is the dominant concern, and it is supply-chain, not code:
whereKey behavior with string keys are untested territory unless we verify.char(36). That is 4.5× the index footprint of bigint and, if it's UUIDv4, random inserts into an InnoDB clustered index — page splits and write amplification at volume.personal_access_tokens, Telescope, Horizon, and many third-party packages assume integer or bigint keys. String PKs ripple into Auth::id() typing, route model binding, and any package doing (int) casts.HasUuids does the same job with framework maintenance guarantees.Key questions for the maintainer/repo audit.
composer.json, and when was the last commit?ramsey/uuid, Str::uuid(), or hand-rolled md5/uniqid (the latter would be disqualifying)?getKeyType/getIncrementing correctly, and does it guard against mass-assignment of id?find() with a non-UUID string — clean miss or SQL error?Stack fit. Do not adopt this as a greenfield choice. The decision matrix:
| Context | Recommendation |
|---|---|
| Laravel 11/12, new models | Native HasUuids (UUIDv7, ordered) — reject the package |
| Laravel 9/10, new models | Native HasUuids (v4) — reject the package |
| Laravel ≤8 legacy app | Package viable, but copy the trait into app/Concerns/ |
| Any stack, existing int PKs | Don't convert — add a uuid column alongside |
Migration path. Two distinct paths, and they should not be conflated:
bigint to char(36) requires dropping every foreign key, altering child columns, backfilling, and re-adding constraints — a multi-hour lock on large tables. The far safer pattern is the dual-key approach: keep the integer PK as the clustered index and add a unique indexed uuid column for external exposure. This gets the API-facing benefit without touching joins or index density.Compatibility. Verify against the target matrix before sequencing any work: PHP 8.1/8.2/8.3, Laravel 10/11/12. Specifically test Route::model() binding, Auth::user()->id typing through Sanctum/Passport, JSON resource serialization, and any package with a hard FK to users.id. If the package's constraints block installation, the correct move is to vendor the trait (it is likely <80 lines) rather than fork the repo — you inherit the behavior without the maintenance surface.
Sequencing.
composer require fails, stop and vendor.find() behavior.Maintenance. Treat this as an owned asset, not a dependency. With 7 stars and 0 dependents, expect zero upstream responsiveness — no CVE patches, no Laravel 13 compatibility, no PHP 9 support. Pinning the version and enabling Dependabot only tells you when it breaks; it doesn't fix it. Copying the trait into application code removes the Composer constraint risk entirely and is the lowest-TCO option for a package this small. If it must stay a dependency, fork it under an org namespace.
Support. No maintainer SLA, no Discord/Slack, no Stack Overflow footprint. Your engineers are the support tier. That is acceptable for ~80 lines of trait code, unacceptable if the team assumes "there's a package for that" and stops reading it.
Scaling. The operational bill arrives at the database. char(36) PKs inflate every secondary index and every foreign key in child tables; random UUIDv4 inserts fragment the InnoDB clustered index and degrade write throughput as tables grow past memory. Read-heavy or low-volume tables won't notice. High-insert tables (events, audit logs, orders) will, and by then the migration is expensive. If UUIDs are needed for external identity, the dual-key pattern (int clustered PK + unique uuid column) is the scaling-safe answer. UUIDv7/ULID is the next-best if a UUID must be the PK.
Failure modes to instrument.
WHERE user_id = '1' against a char(36) column returns nothing, silently, rather than erroring.id as an integer break on deploy.id accepted from a request body could overwrite the generated key if $fillable isn't locked down.Ramp-up. Low for engineers — a trait plus two model properties is an hour of reading. The cost is in convention and review discipline, not knowledge: the team needs a written rule for when UUIDs are used, which storage strategy applies, and a PR checklist item for key type changes. Budget a short internal design note and a single pilot model rather than formal training. Realistic time-to-first-production-model: one sprint including the spike and the compatibility matrix.
How can I help you explore Laravel packages today?