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

Eloquent Public Id Laravel Package

yieldstudio/eloquent-public-id

A Laravel package that adds unique, Stripe-style public IDs to Eloquent models, helping hide auto-incrementing primary keys and create cleaner, safer URLs. Includes configurable prefixes and simple trait-based integration.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

What it is. A trait for Laravel 9+ Eloquent models that attaches an opaque, externally-facing identifier (presumably UUID/ULID) to a model while leaving the auto-increment or bigint primary key intact. This is the "parallel public ID" pattern: internal 8-byte integer keys continue to back joins and foreign keys, while URLs, APIs, and webhooks expose a non-enumerable token.

Architecture fit. Good, and deliberately conservative. It avoids the more invasive alternative of making ULIDs the primary key (Laravel's native HasUlids/HasUuids), which would ripple through every foreign key, pivot table, and index in an existing schema. Because it is a per-model trait, adoption can be scoped to a single domain — public-facing aggregates first — without a schema-wide decision. The pattern also directly serves two recurring product requirements: preventing resource enumeration via sequential IDs and decoupling externally-visible identity from storage implementation.

Integration feasibility. High, but only because the surface area is tiny. Realistically this is 30–60 lines: a booted()/creating hook, a generator call, and a published migration stub. If any behavior is missing or unsuitable — column type, generator choice, backfill handling, route binding — replacing it with an in-house trait is an afternoon of work, not a migration project. That materially caps the downside of the package itself.

Technical risk. The risk is supply-chain and longevity, not engineering. The package has 0 stars, 0 dependents, one release on 2025-04-09, and a "Laravel 9 and above" support claim that is unevidenced for 10/11/12 or PHP 8.3/8.4. Laravel 9 itself is past security support, so the stated floor tells us nothing about current-version testing. Mitigating factors: MIT license permits vendoring/forking, the attack surface is a single trait with no dependencies of note, and there is no runtime service or network surface. The residual risks are (a) schema opinions baked into the migration stub that don't match our conventions, (b) silent breakage on a future Laravel major, and (c) an abandoned upstream while we are mid-rollout.

Key questions to answer before adoption:

  • Which generator is used — UUIDv4, ULID, UUIDv7, or custom? Is it configurable per model? ULID/v7 matters for index locality in InnoDB.
  • What column type does the published migration use (char(36), char(26), binary(16))? Can we override it without forking?
  • Does it generate in application code or via DB default? (Application code is preferable for replica safety and for insert()/upsert paths.)
  • How are pre-existing rows handled — nullable column and lazy generation, or explicit backfill guidance?
  • Does it override getRouteKeyName(), or must we? Does it provide a marker interface for type-hinting and controller signatures?
  • Where is uniqueness enforced — a DB unique index, or an application-level exists() check that races?
  • Is the public ID protected from mass assignment overwrite on update? Does it regenerate on restore/duplicate?
  • Interaction with SoftDeletes: does a unique index on public_id collide when rows are recreated?
  • What is the actual CI matrix (Laravel 10/11/12, PHP 8.1–8.4)? Is there any test suite at all?

Integration Approach

Stack fit. Laravel 9+, any Eloquent model, no infrastructure requirements. Verify against our actual target (likely Laravel 11 or 12) with a throwaway composer require and a two-model spike before committing.

Decision gate first. The real architectural choice is "parallel public ID + bigint PK" versus "ULID primary key." For a mature schema with many foreign keys, this package's approach is correct and far cheaper. For greenfield services, native ULIDs are simpler and dependency-free. Pick one convention org-wide and do not mix — mixed identity strategies are the single most common source of "which ID is this?" bugs in API surface reviews.

Migration path.

  1. Spike in a branch: pin the exact version, publish or hand-write the migration, and inspect the generated trait behavior against the key questions above.
  2. Normalize the schema to our standard column type and add a unique index explicitly rather than trusting the stub.
  3. Roll out in the safe order: add nullable column → backfill in chunks (batched, off-peak, with pt-online-schema-change or online DDL for large tables) → add unique index → optionally enforce NOT NULL.
  4. Attach the trait; keep generation in application code so bulk insert()/upsert() paths get an explicit generator call (Eloquent events do not fire for those).
  5. Update getRouteKeyName(), API resources, route model binding, and any signed URLs or webhook payloads.
  6. Remove the internal ID from every external surface in the same release — otherwise the security benefit is nil.

Compatibility and sequencing. Watch for: bulk inserts bypassing the trait, firstOrCreate/updateOrCreate paths, queued jobs and cached payloads holding old IDs across a deploy, mobile clients caching internal IDs, and soft-deleted rows interacting with the unique index. Sequence by domain, not by table: pilot on one low-traffic public model, measure index size and query plans, then propagate to accounts/billing/public APIs, then freeze internal IDs from external responses, then deprecate the old URL shapes behind a redirect window.

Contingency. Given 0 dependents, plan for abandonment from day one. If the spike passes and we adopt beyond the pilot, fork the trait into an internal namespace or vendor it under app/Support, keep the MIT attribution, and treat upstream as a reference implementation rather than a dependency.

Operational Impact

Maintenance. Treat this as owned code. With one release and no community, nobody else will catch a Laravel 12 or PHP 8.5 incompatibility; we will. Budget roughly a day per major framework upgrade to re-validate, and let Renovate/Dependabot surface constraint conflicts early. The code volume is small enough that a fork-and-patch is measured in hours, which keeps this from becoming a genuine platform liability.

Support. None — no SLA, no security policy, no maintainer responsiveness guarantee (the publisher is an agency, and the package reads as an extraction from internal work). Compensate with a mandatory code review at adoption, an SBOM entry, and an ADR recording the decision, the pinned version, and the fork trigger.

Scaling. The public ID column adds ~26–36 bytes per row plus a secondary unique index — negligible below a few million rows, measurable above ~50M. Generation must be application-side so read replicas stay consistent. A ULID or UUIDv7 generator gives roughly time-ordered inserts and avoids the random-insert page-split behavior that UUIDv4 causes in InnoDB; UUIDv4 on a high-write table is the one scaling pattern worth actively rejecting. Route model binding on public_id must be backed by a unique index or every lookup becomes a full scan — this is the most likely production incident.

Failure modes.

  • Unique-constraint collision on generation — negligible probability, but requires a retry loop rather than a 500.
  • A model added later without the trait, or a bulk-insert path that bypasses Eloquent events, yields NULL public IDs and 500s on lookup. Add a test that asserts every externally-routed model has a non-null public ID.
  • Backfill deadlocks or lock contention on large tables; chunk and schedule.
  • Dual-ID leakage: if internal IDs still appear in admin links, webhooks, emails, or error messages, the anti-enumeration benefit is lost and we have paid the complexity for nothing.
  • Soft deletes colliding with the unique index, and cached/queued payloads referencing stale identifiers across a deploy.

Ramp-up. Hours, not days. Any Laravel developer can read the trait in ten minutes; the real onboarding task is the convention — "public IDs at the boundary, integer IDs internally, never both." Document it in the ADR and onboarding notes, and cover it with two Pest tests: uniqueness under concurrent creation, and route binding resolving by public ID while rejecting the integer ID.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel