eg-mohamed/referenceable
Referenceable is a Laravel package that adds customizable reference numbers to Eloquent models. It offers random, sequential, and template-based generation with placeholders like {YEAR} and {SEQ}, plus collision handling, validation, multi-tenancy, and Artisan commands.
Architecture fit
HasReference) with Eloquent lifecycle hooks, a publishable config file, a counter table (model_reference_counters), and Artisan maintenance commands. This is idiomatic Laravel package design — low conceptual overhead, no service container gymnastics, no queue/event infrastructure required.$referenceStrategy, $referenceTemplate, $referenceUniquenessScope, etc.) keeps policy co-located with the domain model, which is good for discoverability and bad for central governance — we would want an internal convention doc so 20 models don't drift into 20 formats.{YEAR}/{MONTH}/{SEQ}/{RANDOM}/{MODEL} placeholder set covers ~90% of reference-format tickets without custom code.Integration feasibility
composer require + php artisan referenceable:install scaffolds config and tables; per-model work is one migration column, one trait line, and optional property overrides. Realistic estimate: half a day for a spike, one to two days to productionize the first model, then near-zero marginal cost per additional model.referenceable:generate --dry-run then --batch=500. That materially de-risks adoption on an existing table, which is usually the hardest part of reference-number projects.reference column or its unique index for us, so the rollout must be sequenced with our own migrations.Technical risk
model-reference → referenceable at v2.0.0, and the README still contains a stale MohamedSaid\ModelReference\ModelReference import in the batch-operations section. Documentation drift of this kind usually correlates with untested code paths — the batch API in particular should be verified by hand before we rely on it.use_transactions plus a retry collision strategy implies row-level contention. Under parallel writes this either serializes on the counter row or produces retries; with max_retries = 100 and no explicit advisory-lock language in the docs, I want to see the implementation before putting a high-throughput table behind it.regenerateReference() is a loaded gun. It is a first-class public method and a referenceable:regenerate --all command exists. Any reference that has escaped the database (emails, PDFs, customer support tickets, external integrations) becomes permanently dangling. This needs policy, not just tooling.cache_ttl is commented "Cache for 60 minutes" against a value of 60, which is more plausibly seconds. Small, but it is the kind of ambiguity that turns into a production surprise.Key questions
creating (same transaction as insert) or a separate step? What happens to the counter if the insert subsequently fails — do we get gaps, and is that acceptable to finance/audit?lockForUpdate / atomic update), or read-then-write? This determines whether we can use sequential on a high-write table at all.counter_table config with no mention of tenant partitioning — how does uniqueness_scope = 'tenant' interact with sequence numbering?Stack fit
uniqueness_scope = 'tenant', tenant_column), which is the deciding factor if we're on a shared-schema multi-tenant model. If we're on database-per-tenant, the feature is inert but harmless.Migration path
ReferenceGenerator interface with the package behind a thin adapter. This is the single highest-leverage TPM decision here: it costs ~1 day and makes the "maintainer abandons the package" scenario a swap, not a rewrite.reference as nullable with a plain index on the pilot table; deploy; backfill via referenceable:generate --dry-run → review → real run in batches; then add the UNIQUE constraint in a subsequent deploy (avoids a locking ALTER on a large table and avoids failing on pre-existing NULLs).referenceable:export of formats and a one-page note on how to reimplement each strategy in ~100 lines if we fork.Compatibility
composer.json constraints against our pinned Laravel/PHP versions before committing — the "Laravel 13" claim suggests the constraint may be permissive (^10|^11|^12|^13) and permissive constraints are how untested combinations reach production.ModelReference will not work. Greenfield for us, so low impact — but do not copy README snippets verbatim; the batch-ops example is stale.Sequencing
sequential reserved for low-to-moderate write-volume tables until the locking question is answered.Maintenance
referenceable:validate --fix helps but does not verify that the sequence matches the max existing reference — that check should be manual and periodic).Support
Scaling
random and template-with-{RANDOM} strategies scale horizontally with no coordination — preferred default.sequential is a single hot row per counter scope. At high insert rates (thousands/sec) expect contention and retry storms; at hundreds/sec it is fine. If reset_frequency is daily/monthly, also plan for a midnight boundary — a burst of writes at period rollover hits an uncontended-but-cold counter path that is worth load-testing.cache_config on reduces per-request config resolution cost; ensure it is enabled in production and that config changes are followed by a cache clear (a stale TTL is a classic "why didn't my format change" incident).--batch=500 and run off-peak.Failure modes
retry strategy with max_retries = 100 on a short random reference is a silent saturation risk. Enforce a minimum entropy floor (length ≥ 6 with the excluded-character set) and monitor insert failures.regenerate --all run breaks every externally distributed reference. Mitigate by not exposing the command in runbooks, and by restricting it to pre-go-live records.tenant_column or scope downgraded to model means two tenants can hold the same reference, and cross-tenant lookup by reference silently returns the wrong record. This is the highest-severity failure mode and deserves an integration test.reference column, or with the column in $guarded, fails quietly at the wrong moment. Assert on reference presence in model factories/tests.Ramp-up
How can I help you explore Laravel packages today?