juststeveking/laravel-key-factory
A Laravel package for generating unique, customisable keys for Eloquent models. Configure prefix, length, and allowed characters, then assign keys through a simple trait—ideal for API tokens, order numbers, and public IDs.
Architecture fit. The package is a micro-utility that binds a key generator to an Eloquent model through a HasKeyFactory trait plus a config map of model class → generator (e.g. Ulid::class). It operates in the model layer, not the application identity layer, which means it sits below the product decisions we actually care about — customer-visible identifier strategy, API contracts, and credential handling. The decisive architectural fact: Laravel has shipped HasUuids/HasUlids natively since v9, so the functional surface here is largely redundant with the framework. Its value from a TPM's seat is as a pattern precedent — concrete proof that per-model key strategies are cheap for new models — not as a runtime dependency. Before anything else, resolve the framing ambiguity: the name and description have been read both as an Eloquent primary-key factory and as an API-token generator. These are different decisions with different risk profiles and different native alternatives (ULIDs vs. Sanctum/Passport). If the real need is credentials, this package is the wrong answer outright.
Integration feasibility. Mechanically trivial for any Laravel team: composer require, publish the config, apply the trait, match the migration column type, smoke-test that Model::create([])->id returns a 26-character ULID rather than an integer. That is roughly an afternoon of work. The blocker is confidence, not effort — last release November 2022, six stars, zero dependents, single-maintainer history, no visible security policy, no downstream users to learn edge cases from. Against a current Laravel/PHP matrix, assume stale Composer constraints until proven in a scratch branch.
Technical risk. Moderate-to-high, and concentrated in a high-impact failure domain. The blast radius is small because the code surface is tiny, but key generation is correctness-critical: duplicate keys under concurrency or bulk inserts, column-length/index/collation mismatches, trait or model-event conflicts that silently alter the model lifecycle, and breakage on a future framework upgrade with no upstream patch path. For a long-lived or regulated codebase the risk calculus is unfavorable — we would be trading an unmaintained dependency against roughly 100 lines we own and control.
Key questions.
HasUlids or Sanctum.ulid('id')->primary() vs. uuid vs. prefixed string)?Stack fit. Laravel/Eloquent only — not applicable to non-Laravel PHP or other ORMs. Within Laravel, scope it narrowly to a small number of models with a specific key requirement. Do not position it as an application-wide identity standard; the platform default should be framework-native.
Migration path. Do not introduce broadly. The correct sequence: (1) read the source and confirm the exact generation algorithm; (2) validate composer require against the current Laravel/PHP matrix in a scratch branch; (3) prototype on one non-critical model and smoke-test the ->id output and column type; (4) add collision/concurrency/bulk-insert tests; (5) benchmark against native HasUlids/HasUuids and a ~100-line first-party implementation; (6) decide adopt / fork / reimplement; (7) if adopted, pin the version and name an owner in writing. Note explicitly in the plan that the genuinely hard part of changing key strategy — backfilling and dual-writing existing tables — is not solved by this or any key-generation package and must be estimated as separate, larger work.
Compatibility. Verify Composer constraints first, before any design work. Check HasKeyFactory for conflicts with existing model traits, observers, and boot methods. Confirm column length, index type, and collation assumptions for the chosen format before writing migrations. Run the full application suite plus queue-worker and model-factory tests, since generation fires on model creation and will surface in factories and seeders first.
Sequencing. The right order is to adopt Laravel-native ULIDs as the platform default for new externally-visible models first, and cite this package as precedent in the ADR — not as a rollout step. If a per-model generator override beyond native capability is genuinely required, commission a first-party implementation behind an internal interface rather than taking the supply-chain dependency. In parallel, if the real requirement is credential-style keys, standardize on Sanctum/Passport's token model (issuance, hashing, abilities, revocation) and close out this evaluation. Record the whole call in an ADR so teams stop solving identity strategy individually.
Maintenance. Low code-maintenance, high ownership risk. The adopting team effectively becomes the maintainer. Version pinning reduces surprise but does not remove the obligation to re-validate on every Laravel/PHP upgrade. A fork under the org namespace, or a small internal reimplementation, is the more sustainable operating model — and at ~100 lines the reimplementation is cheaper than the ongoing re-validation of an unmaintained dependency.
Support. Minimal. No evidence of an active maintainer group, no visible security policy, no downstream users to learn from. Expect to debug from source rather than rely on upstream fixes or issue triage. Budget for that in the estimate; it is the hidden cost that makes the "free" dependency expensive.
Scaling. Generation is local and computationally cheap — it will never be a throughput bottleneck. The scaling concern is correctness, not performance: uniqueness must hold under parallel workers, bulk imports, retries, and queue fan-out. Validate with an explicit concurrency test before any broad rollout, and confirm whether uniqueness is enforced by the generator, a unique index, or both.
Failure modes.
Ramp-up. Low for a Laravel developer — install, publish config, apply trait, match migration, test. The real ramp-up is organizational: establish ownership, write the ADR, and gate any spread across models behind review. For a TPM the deliverable is the decision, not the package — move customer-visible identifiers off sequential integers, standardize on the framework default, and scope retrofit of existing tables separately. Per-service cost is roughly one engineer-day for new work; the backfill estimate is the number that will actually shape the roadmap, and no key-generation package changes it.
How can I help you explore Laravel packages today?