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

Configuration Bundle Laravel Package

sherlockode/configuration-bundle

A Symfony bundle for database-backed application configuration. Define typed settings, manage them through an admin-friendly interface, and retrieve values easily in services, controllers, and templates, with Doctrine persistence and caching support.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the honest framing. sherlockode/configuration-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/configuration.php publish tag. As a Laravel developer you have three real options, in order of cost:

  1. Pattern-borrow — read the schema/type/manager design and implement the same shape against config() + Eloquent (or drop in spatie/laravel-settings, which is the Laravel-native equivalent and should be your default unless you specifically need this bundle's type system).
  2. Sidecar — run the bundle inside a small Symfony service that exposes configuration as JSON over HTTP, consumed and cached by Laravel.
  3. Doctrine-in-Laravel — pulling Doctrine ORM into a Laravel app purely for a settings table is not worth the integration tax. Reject this unless Doctrine is already present.

If you are evaluating it for a Symfony service in a mixed estate, the minimal path is:

composer require sherlockode/configuration-bundle
  • Confirm the platform floor: PHP 8.2–8.4, Symfony 6.4/7.x (that is exactly what v0.8.0 pins). Anything on Symfony 5.4 / PHP 8.1 is stuck on 0.7.x.
  • Register the bundle in config/bundles.php.
  • Ensure Doctrine ORM is configured — the bundle persists values through entities, so you need a connection plus the schema (migrations or doctrine:schema:update in dev).

Where to look first, in this order:

  1. The README's "usage" section for the exact ConfigurationManager method names — the public surface is deliberately tiny (read one, read all, write one) and you should verify signatures rather than assume them.
  2. The type system directory. This is the part worth studying: each type knows how to validate and (de)serialize its value, and the registry is what you extend.
  3. The Doctrine entity mapping — you need to know how array/json values are stored before you write any query or migration around them.

First use case: the "site name / support email / feature flag" trio. Declare three entries in code, let the DB hold the values, inject ConfigurationManager into a controller and a Twig extension, then build one trivial admin form that writes them back. That exercises the whole loop — schema in code, value in DB, read at runtime — in under an hour.

Implementation Patterns

Schema in code, values in the database. The bundle's core discipline is that definitions (code, stable, versioned) are separate from values (DB, mutable, admin-editable). Keep that boundary religiously: never let an admin create a brand-new configuration key from a form. New keys ship as code; only values are editable.

Wrap the manager behind your own interface on day one. The single highest-value decision. Do not let ConfigurationManager leak into controllers, jobs, or Blade/Twig:

interface AppSettings
{
    public function siteName(): string;
    public function supportEmail(): string;
    public function newCheckoutEnabled(): bool;
}

final class BundleBackedSettings implements AppSettings
{
    public function __construct(private ConfigurationManager $manager) {}

    public function newCheckoutEnabled(): bool
    {
        return (bool) $this->manager->get('checkout.new_enabled');
    }
}

Fifteen minutes of work, and it is the difference between "we can swap this out" and "this bundle is now load-bearing in 40 call sites." It also gives you a seam to fake in tests.

Cache-aside is not optional. Configuration reads land on the request path. Left raw, every flag check is a DB round-trip — and a page render might do a dozen. Decorate the manager:

final class CachedConfigurationManager implements ConfigurationManagerInterface
{
    public function get(string $code): mixed
    {
        return $this->cache->rememberForever(
            "cfg:{$code}",
            fn () => $this->inner->get($code)
        );
    }

    public function set(string $code, mixed $value): void
    {
        $this->inner->set($code, $value);
        $this->cache->forget("cfg:{$code}");
    }
}

Two things to get right: invalidate on write (the setter above, not a scheduled job), and use a shared cache store if you run more than one app server — a per-node APCu cache means an admin edit takes effect on one node and silently not on the others. Alternatively version the whole namespace (cfg:v{int}:...) and bump the version on any write; cruder, but it survives multi-node without a taggable store.

Bulk-load the whole set once per request. If you are going to touch more than a couple of keys, read everything in one call and serve from an in-memory array. It turns N queries into one, and the configuration set is small enough that this is always the right trade.

Admin write path. You get no admin UI from this bundle — you build it. The pattern that ages well:

  1. A form type whose fields are generated from your declared configuration definitions (not hand-written per setting).
  2. Validate with the declared type before persisting — the bundle's type system is your validation layer, so use it rather than $request->validate()-style ad hoc rules.
  3. Persist, then fire an event or call a cache invalidator.
  4. Write an audit row (old value, new value, actor, timestamp). The bundle does not do this for you, and you will want it the first time someone asks "who turned off checkout?"

Migration pattern from static config. Read-through fallback while transitioning: if the DB has no value for a code, fall back to the old config()/YAML/env source, and log which path was hit. Run both for a release cycle, then delete the fallback. Never migrate everything at once, and never migrate secrets — DB config tables are visible in admin panels and DB backups.

Multi-tenancy is not built in. The bundle is a flat key/value store. Tenant-scoped settings require either a custom type/entity extension or a per-tenant key prefix (tenant.{id}.site_name) with a decorator that injects the tenant ID into every lookup. The decorator approach is far cheaper; take it unless you have genuinely large per-tenant config volumes.

Custom types are the intended extension point — register a type service that knows how to validate and (de)serialize your format (a money value, a timezone, a JSON schema). That is how you keep validation in code while values stay editable.

If you take the sidecar route, expose GET /config returning the full resolved set as JSON with an ETag; have Laravel poll it into its own cache on a short TTL and read locally. That keeps configuration off the Laravel request path entirely and makes the Symfony boundary explicit and replaceable.

Gotchas and Tips

  • Pre-1.0 semver. 0.7.1 → 0.8.0 was permitted to break, and did narrow the supported platform matrix. Pin the exact version, commit the lockfile, and add a CI composer update --dry-run resolution check so a constraint-only release cannot surprise you on deploy.
  • No LTS, one maintainer. Do not make this a tier-1 dependency without a named internal owner and a documented fork plan. It is ~31 stars; the codebase is small enough that forking is genuinely viable, which is the main reason the risk is acceptable.
  • Reads fail hard when the DB is unavailable. A settings lookup in a constructor or a Twig extension means a database blip becomes a 500 on every page. Wrap reads with a last-known-good fallback (a file cache or a hardcoded default), or make the accessor return the declared default rather than throwing.
  • Treat configuration codes as immutable identifiers. Renaming a code silently orphans the stored row and the app reverts to defaults with no error. Renames need an explicit data migration, so budget for them or accept permanent naming weirdness.
  • Defaults are your bootstrap story. Before migrations run, or in CI with a fresh DB, resolution should fall back to the declared default. Verify that behaviour explicitly — it is the difference between "the app boots on an empty database" and "the app cannot boot until someone sets a site name."
  • Understand how array types are serialized before you write any query against the table, and before you ever hand-edit a value in SQL. JSON is queryable and diffable; PHP serialize() is neither.
  • Check the flush contract. Some managers flush the EntityManager on set(), some expect you to. Getting this wrong produces values that appear saved in the request and vanish afterwards, or unexpected flushes inside a larger transaction.
  • No built-in audit or rollback. Add a version table yourself. It is ~30 lines and it is the single most requested feature of every settings system ever built.
  • Don't cache invalidation by TTL alone. A five-minute cache on a "kill switch" flag means five minutes of the feature still being live after someone pulls it.
  • Keep an allowlist of migratable domains — feature flags, display settings, notification thresholds. Explicitly exclude API keys, credentials, and anything security-sensitive. A DB config table is a bad secrets store and a worse blast radius.
  • Debugging tip: when a value "won't change," check in this order — is the code correct, is the row actually present in the table, did the write flush, is the cache invalidated, is the cache shared across nodes. That order resolves almost every report, and the third and fifth items are the ones people miss.
  • Extension points worth knowing: custom configuration types (the type registry) and decorating the manager. Both are supported patterns; neither requires forking. Reach for those two before you touch vendor code.
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.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core