2lenet/config-bundle
Symfony bundle that gives your app an easy, flexible configuration system. It ships traits and an abstract repository to build your own config entity, plus a ready-to-use cache manager. A perfect companion to the Crudit bundle.
Architecture fit
The first item on the decision log is a framework correction: this is a Symfony bundle, not a Laravel package. The namespace is Lle\ConfigBundle, it registers against the Symfony kernel, depends on Doctrine ORM (resolve_target_entities), imports routes from config/routes.yaml, and ships Twig helpers. There is no service provider, no Eloquent model, and no artisan surface. Any plan that assumes "install and configure" on a Laravel codebase is invalid before estimation begins.
What the package is, functionally, is a database-backed settings table with a typed facade: a Config entity composed from ConfigTrait, a repository extending AbstractConfigRepository, a CacheManager layered over reads, a lle:config:warmup console command, and a get_config_value('type','group','label','default') Twig function. Supported types per the README are bool/string/text/int; release notes extend that to float (1.3.0), password (1.2.6–1.2.7), and translatable labels (1.4.0–1.4.2). It is designed as a companion to the same vendor's CruditBundle for inline admin editing.
That is a pattern, not a platform. The valuable IP is roughly 300 lines of obvious code plus a caching decision that we would have to audit anyway. For a Laravel target the correct framing is: port the pattern; do not consume the artefact.
Integration feasibility
composer.json, Config entity using the trait, repository subclass, one doctrine.orm.resolve_target_entities block, one routes import, make:migration, and a warmup step in deploy. Call it 0.5–1 engineer-day, plus review time.settings table (group, label, type, value, nullable tenant_id, timestamps, unique index on group+label+tenant_id), a cached accessor service, a model observer that busts the cache tag on write, an artisan settings:warmup command, and a Blade helper mirroring get_config_value.Technical risk
Establishment relation to the Config entity, implying per-tenant rows. The accessor signatures (getBool($group, $label, $default)) carry no tenant discriminator, so if CacheManager keys on group/label alone, tenant A can be served tenant B's configuration. This must be resolved before any code is written, in either the adopt or port path.password type exists, but there is no documented encryption, key management, RBAC, or audit trail. Settings-as-DB-rows also means secrets leave the codebase and enter the backup/restore and DB-access blast radius. Prior positioning that claimed audit trails and versioning is not supported by the README and should be struck from the business case.repositories wrapper). Single-vendor authorship, and the org (2lenet) and PHP namespace (Lle) disagree — a naming smell that suggests an internal rename never propagated. There is no external contributor base to absorb maintenance.getInt($group, $label, string $default): int has a type-inconsistent default parameter — a small signal that the docs are not compiled or tested against the code.Key questions
CacheManager cache key? If it does not include tenant identity, the package is disqualified as-is and the port path becomes mandatory.CacheManager use a shared pool (Redis/memcached) or per-process cache, and does set*() invalidate explicitly? Cross-node staleness is the difference between "works in staging" and "config changes require a rolling restart."Stack fit
Recommend pattern adoption, not package adoption, for any non-Symfony target. For Laravel, the concrete shape is: settings table with type discriminator and nullable tenant_id; a SettingsService reading through Cache::rememberForever keyed on tenant:group:label:type; a model observer or explicit invalidation on write; php artisan settings:warmup invoked in the deploy pipeline; a Blade directive mirroring get_config_value. This preserves every stated benefit — DB-backed, admin-editable, typed, cached — while eliminating the Doctrine coupling and the unsupported dependency.
If the target is Symfony and already uses Doctrine, direct adoption is defensible at ~1 day, provided the cache-key question in the risk section is answered first. In that case, pin to the 1.4.4 tag and vendor by commit SHA rather than tracking a branch, since there is no Packagist release to give you a stable constraint.
Migration path
AbstractConfigRepository and CacheManager source directly. There is no substitute: the README does not describe cache keying, invalidation, or the auto-create behavior in enough detail to plan against. Output: a written answer to the tenant-key question.ConfigReader) so the storage backend can be swapped later. Seed from any existing config/ values, and keep env-specific values in env, not in the table.Compatibility
Symfony 7 support landed in 1.2.0, so the bundle is current with modern Symfony. Doctrine ORM mapping style in the examples uses annotations (@ORM\Entity), which is deprecated in favor of attributes in Doctrine 2.9+/ORM 3 — expect to rewrite the README's entity sample. For the port path, Laravel compatibility is a non-issue; PHP version is the only constraint and should match the host application.
Sequencing
Do not sequence this alongside any other config, caching, or deployment-pipeline work. The CacheManager audit and the warmup-in-deploy step both touch the release process, and a bad cache key ships silently — it looks correct in a single-tenant dev environment and fails only in production. Land in this order: spike → decision gate → implementation → observability (cache hit/miss and invalidation logging) → warmup in pipeline → admin UI (Crudit or a simple Laravel resource). The admin UI is genuinely last: the README's editing experience is coupled to Crudit, and the Crudit dependency should not be inherited by a Laravel port.
Maintenance
Adopting the package transfers a small but real maintenance burden to a vendor with an unclear commitment and no external contributor pool. Budget for the possibility that a Symfony or Doctrine upgrade breaks it and the fix is yours to write and upstream. The port path inverts this: ~4 days up front buys a ~150-line surface your own team owns outright, with no upstream surprise risk. For a component this small, ownership is cheaper than dependency — that is the core TPM judgment here, and it is reinforced by zero dependents and no Packagist presence.
Support
No documented support channel, SLA, or security disclosure process. A password type with no documented encryption suggests security review was not part of the release process; treat any secret-handling feature as unaudited. If the package lands in a production path, the operating team needs a named internal owner, because there is no external escalation route.
Scaling
The workload is trivial: a small settings table, read-mostly, fronted by cache. Database load is negligible; the scaling question is not throughput, it is fan-out correctness — whether every app node converges on the same value after an edit. With a shared cache pool and explicit invalidation, fine. With per-process cache and no shared pool, config changes require a rolling restart, which silently converts a settings edit into a deploy. In multi-tenant deployments, the cache key is the scaling story: a key that omits tenant identity does not degrade gracefully, it serves wrong data at any scale.
Failure modes
group+label(+tenant_id) and an upsert.Ramp-up
Low. The public surface is five or six getters, one setter family, one Twig function, and one console command — an engineer is productive in under half a day. The non-obvious knowledge is all in the cache layer, which is undocumented; that knowledge should be captured in a short internal note during the spike rather than rediscovered per incident. Net recommendation: score this low as a dependency, score the pattern as useful, and spend the 2–4 days to own it.
How can I help you explore Laravel packages today?