makinacorpus/preferences-bundle
Symfony bundle for managing user preferences: define configurable preference keys, persist per-user values, and read or write settings through a simple API. Integrates with Symfony apps to centralize user-specific configuration.
Architecture fit — the package does not fit, but the problem domain does. Despite being catalogued here as a Laravel/PHP package, makinacorpus/preferences-bundle is a Symfony bundle: it ships a Bundle subclass, a DI extension, and (almost certainly) Doctrine ORM mappings. Laravel has no bundle or compiler-pass lifecycle to hook into, and its container is a runtime resolver rather than a compiled one. Laravel does already ship a set of Symfony components (HttpFoundation, Console, Routing, Process, Mime, Translation, ErrorHandler, VarDumper, Finder), so a parallel-container bootstrap is theoretically possible — it is not something a TPM should approve for a preference store.
The caveat that makes this worth an evaluation slot at all: the capability is real and recurring. "Configuration that users edit at runtime, and which therefore cannot live in env vars or container parameters" is a genuine structural gap in most PHP applications. The opportunity: 7.51 score reflects the problem space. A TPM should treat this artifact as a reference design and a decision instrument, not a candidate dependency.
Integration feasibility — low, and three paths exist.
| Path | Effort | TPM verdict |
|---|---|---|
| Install as-is via a Symfony-in-Laravel shim | 3–6 weeks + permanent ops debt | Reject |
Fork, extract the domain core, rebuild wiring as a Laravel ServiceProvider |
2–4 weeks incl. tests | Only if the scoping model is genuinely novel to us |
Build natively (Eloquent + cache + typed casts) or adopt spatie/laravel-settings |
3–7 days | Recommended |
Technical risk — high, on four axes.
symfony/framework-bundle ^4/^5 and doctrine/orm ^2 — installing it either fails resolution or forces a downgrade of shared Symfony components, which is a whole-application risk rather than a local one.Key questions to resolve before any code is written
symfony/framework-bundle, or only doctrine/orm plus a manager class? This moves feasibility from "weeks" to "days."require/require-dev constraints resolve against our composer.lock?Stack fit — state the mismatch plainly to stakeholders. This is a near-zero-adoption Symfony bundle being evaluated for a Laravel codebase. The defensible TPM position is: we are evaluating the pattern, not the artifact. If we proceed, we proceed against a fork we control, never against upstream as a Composer dependency.
Recommended move before any fork. Audit spatie/laravel-settings (MIT, maintained, typed settings classes, migrations, per-model scoping) or a ~200-line internal settings table behind a cached repository interface. Either delivers ~90% of the value in a week. The only justification for forking is if this bundle exposes a specific data model — hierarchical scoping, a preference-definition registry — that we would otherwise design ourselves and consider differentiating. That is the question the spike must answer.
Migration path — greenfield only. Zero dependents means no installed base and nothing to migrate from. The migration is inward: if we fork, carry the schema into a Laravel migration, move the domain classes into app/ or a packages/ path repository, and pin by commit SHA (upstream will not produce release tags we can rely on).
Compatibility gates, in order:
composer why-not makinacorpus/preferences-bundle against the current lock — this alone may end the evaluation in twenty minutes.Serializable patterns.symfony/framework-bundle; if present, the fork must strip it.Sequencing (if approved):
PreferencesServiceProvider with bindings, publishable config, and a facade.Illuminate\Contracts\Cache\Repository.PreferenceStore interface so the fork is swappable for a maintained package. This interface is the single most important deliverable — it caps the downside of the entire bet at one adapter swap.Maintenance — we become the maintainer. With one star and no dependents there is no upstream to consume patches from and no realistic path to upstreaming a Laravel adapter. Budget 10–15% of one engineer's time in perpetuity for security review, PHP/Laravel major bumps, and bug response. Mitigate by keeping the fork thin: domain core only, Laravel wiring in our own namespace, a documented diff against upstream. If our own code grows past a few hundred lines, we have written the package and should drop the pretense and own it outright.
Support — none, by any channel. No vendor SLA, no reachable maintainer for a bundle of this footprint, no advisory feed. A support relationship with Makina Corpus would be a commercial consulting engagement and should be priced into the decision, not assumed. spatie/laravel-settings has paid support and a responsive maintainer — that asymmetry belongs in the comparison slide.
Scaling — the read path is the entire story. Preference resolution happens on every request for every user, which makes it one of the hottest queries in the system. Three concerns: (a) cache is mandatory, not an optimization — a per-request DB lookup per preference key is an N+1 hazard and will not survive load; (b) invalidation on write must be correct across processes, which rules out request-scoped memoization alone; (c) cache stampede — a deploy or flush triggers a synchronized miss across the fleet for the most popular keys, so plan for lock-based rebuild or jittered TTLs. Also model the growth curve: preference rows are user-generated and unbounded, so the unique index on (scope, scope_id, key) and the resolver query plan are first-class design artifacts. Multi-tenant scoping multiplies cache key cardinality — confirm the key includes the tenant dimension or you risk cross-tenant leakage, a security incident rather than a bug.
Failure modes to instrument for explicitly
"false" is truthy) when values round-trip through JSON or strings.Ramp-up — deceptively shallow. A competent Laravel engineer can reproduce a typed scoped key/value store with cache in a week; there is no exotic technology here. The difficulty is not the code, it is the governance taxonomy the code encodes: separating deploy-time config, global admin settings, user preferences, feature flags, and secrets, and defining the resolution order (user → tenant → global default) before surfaces proliferate. Expect one to two days of design alignment with product and security before the first line is written. The bundle is most useful as input to that alignment conversation — a concrete, readable prior art that forces the team to answer "what is a preference, what is a flag, and what is a secret?" That is the real return on this evaluation, and it is available without taking the dependency.
How can I help you explore Laravel packages today?