kadevland/simple-laravel-eloquent-uuid
A lightweight Laravel package that adds UUID primary keys to Eloquent models with minimal setup. Use a simple trait to generate UUIDs automatically, keep models clean, and avoid manual ID handling in your database.
Architecture fit. The package is a single Eloquent trait that hooks the model's boot lifecycle to assign a UUID on create, typically paired with $incrementing = false and $keyType = 'string'. That is a narrow, well-understood seam in Laravel — it composes with mass assignment, route model binding, and relationships without touching the query builder. Fit is high in principle, but the total functional surface is roughly 30–60 lines of code. This is a "commodity capability" dependency, not an architectural component.
Integration feasibility. Adoption is a composer require, a use statement on each model, and a migration changing the primary key column to a 36-char string (or 16-byte binary). Effort is measured in hours, not sprints. There is no service, no config surface, no external runtime dependency beyond a UUID generator.
Technical risk. Material, and mostly not about correctness:
HasUuids / HasUlids (and HasUniqueStringIds), plus Str::uuid(). A third-party trait from 2020 competes with a framework feature that is maintained, tested, and documented by the vendor. Build-vs-buy strongly favors build or buy-the-framework here.ramsey/uuid range that can conflict with other packages in a mature composer.lock.Key questions for the maintainer / spike.
$incrementing/$keyType itself, or expect the developer to?insertOrIgnore, upsert, chunked inserts, and raw/DB-level inserts that bypass model events?boot<Trait> hooks on the same model?TPM verdict. Do not add this dependency to a new or actively maintained codebase. Replace with the framework's built-in UUID trait (or a ~40-line in-house trait) at effectively zero incremental cost. The package is defensible only for a frozen legacy Laravel 6/7 application where the cost of change exceeds the cost of carrying an unmaintained dependency — and even then, vendor or fork it rather than relying on Packagist.
Stack fit. Best-case fit is a Laravel 6/7 application on PHP 7.x, MySQL/Postgres, with a small number of models needing opaque public identifiers. Poor fit for Laravel 9+, where native support removes the reason to exist.
Build vs. buy decision, made explicitly. Three options, in preference order:
HasUuids / HasUlids on Laravel 9+ (ULIDs/UUIDv7 preferred for index locality). Zero dependency, vendor-tested.Str::uuid(), owned by the team, testable against our own model matrix.Compatibility work before commitment. Pin a throwaway branch and run: composer require against our locked dependency graph (watch for ramsey/uuid conflicts), a PHP/Laravel matrix in CI, and a model-level test covering create, upsert, insertOrIgnore, route binding, JSON serialization, and relationship hydration. This is a half-day spike and it will answer most of the open questions above.
Migration path — the real cost center. The trait is trivial; the schema and data migration is not.
CHAR(36) is simple and readable but bloats every index and every foreign key by 4.5×. BINARY(16) is compact but requires conversion at the model boundary and complicates ad-hoc SQL and BI tooling. Choose deliberately; changing later is a second migration.uuid column alongside the existing auto-increment PK, backfill, update application reads and route binding, then promote. This avoids a big-bang PK swap and keeps foreign keys intact. Dropping the integer PK is a third step, not part of the first deploy.orderBy('id'), cursor pagination, and any analytics or export that assumes monotonic IDs. This is the most common silent regression.Sequencing.
uuid column; backfill job (1–2 days, mostly data).Maintenance. Treat the package as unmaintained. Concretely: add it to the dependency watchlist, assign an owner, and be prepared to fork or inline the trait on the next Laravel major. The ongoing cost is not fixing bugs — it is the recurring review burden of justifying an abandoned dependency in every security and upgrade audit.
Support. Community-only, single maintainer, no SLA, no disclosure channel. Escalation path is "read the source" — acceptable for 50 lines of code, unacceptable if the team does not have PHP/Laravel depth to own the fork.
Scaling. This is where the choice of UUID flavor becomes an infrastructure decision:
Failure modes to design against.
upsert, insertOrIgnore, chunked inserts, and any raw SQL or DBA-side write will not trigger the trait, producing NULL or duplicate keys. Enforce a DB-level NOT NULL + UNIQUE constraint as a backstop.boot<Trait> hooks or a generateUuid method; Laravel silently resolves some of these badly. Test on the actual model set.Ramp-up. Developer learning curve is near zero — one line per model, well-understood semantics. The hidden ramp-up is organizational: DBAs and analysts need to accept opaque, non-sortable identifiers in queries, dashboards, and support tooling. Budget for that conversation; it is usually the real source of friction, not the code.
How can I help you explore Laravel packages today?