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

Config Bundle Laravel Package

smirik/config-bundle

Symfony bundle for managing application configuration from the database. Provides Doctrine-backed settings storage, a service for reading and writing config values, caching, and integration with Symfony services and templates so runtime options can change without code deployments.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the mismatch before you plan. smirik/config-bundle is Smirik\ConfigBundle — a Symfony bundle with a DependencyInjection extension, Resources/config YAML service definitions, and container-parameter config merging. There is no Laravel service provider, no facade, no config/config-bundle.php, and no package auto-discovery. In a Laravel codebase this is a reference design, not a dependency. Day-to-day leverage means porting its ideas onto Eloquent + Cache, not calling it.

Read the source in this order (worth 20 minutes, it is small):

  1. DependencyInjection/*Extension.php — see how defaults are merged with app config.
  2. The entity/repository for config rows — the key/value schema is the actual contract.
  3. The service that resolves a key to a typed value — copy the resolution order.
  4. The Twig extension — indicates intended read patterns (per-render lookups, so caching matters).

Check compatibility first if you still want to try it (e.g. in a legacy Symfony app you maintain alongside Laravel):

composer why-not smirik/config-bundle ^1.0
composer show smirik/config-bundle --all   # PHP + symfony/* constraints, licence, tags

Expect PHP 5/7 and symfony/* 2.x/3.x pins. If it requires symfony/http-kernel 2.x/3.x, it is incompatible with Laravel 9+ by construction.

First use case (Laravel-native rebuild). Reproduce the capability you actually want — DB-backed, environment-aware, cached runtime settings:

// database/migrations/..._create_settings_table.php
Schema::create('settings', function (Blueprint $t) {
    $t->id();
    $t->string('environment')->default('production')->index();
    $t->string('key');
    $t->text('value')->nullable();
    $t->string('type')->default('string'); // string|int|float|bool|json
    $t->timestamps();
    $t->unique(['environment', 'key']);
});
final class Settings
{
    public function __construct(private Repository $config, private string $env) {}

    public function get(string $key, mixed $default = null): mixed
    {
        // 1. code default  2. env override  3. DB override
        $rows = Cache::rememberForever("settings:{$this->env}", fn () =>
            Setting::where('environment', $this->env)->pluck('value', 'key')->all()
        );

        if (! array_key_exists($key, $rows)) {
            return $this->config->get("settings.{$key}", $default);
        }

        return $this->cast($rows[$key], $this->config->get("settings.{$key}.type", 'string'));
    }
}

Bind it as a singleton in AppServiceProvider; that is your "bundle registration" step.

Implementation Patterns

Resolution order is the whole design. Encode it explicitly and document it: code default → env override → DB override → request/runtime override. The bundle's value is that precedence is predictable; if you rebuild it, write it down in a docblock and test it. Silent wrong values come from an ordering you cannot recite.

Back the repository, don't scatter reads. Either inject Settings where needed or implement Illuminate\Contracts\Config\Repository and swap it in the container. The latter keeps config('limits.max') working across the app:

$this->app->singleton('config', fn ($app) => new CachedConfigRepository(
    $app->make('settings'), $app['config']
));

Cache aggressively, invalidate on write, never on read. Settings sit on the per-request hot path. Cache the whole environment map in one key, not per-key (avoids N round-trips). Invalidate via model events:

static::saved(fn () => Cache::forget("settings:{$this->environment}"));
static::deleted(fn () => Cache::forget("settings:{$this->environment}"));

Use a shared cache store (Redis) so all app servers see the same invalidation.

Type casting is a first-class concern. Persist type, cast on read. (bool) "false" is true; guard it:

private function cast(?string $v, string $type): mixed
{
    return match ($type) {
        'bool'  => filter_var($v, FILTER_VALIDATE_BOOLEAN),
        'int'   => (int) $v,
        'float' => (float) $v,
        'json'  => json_decode($v, true, flags: JSON_THROW_ON_ERROR),
        default => $v,
    };
}

Validate on write, not on read. Use a FormRequest and a schema map keyed per setting (max_upload_mb => ['integer', 'min:1']). Fail the write loudly; never let a typo (limts.max) silently fall back to default in production.

Scope it. The bundle's per-environment overrides map cleanly to an environment column. For multi-tenant, add tenant_id (nullable = global) and resolve global → tenant in the lookup. Defaults live in code; overrides live in data.

Gate the write path. Put the CRUD (Filament/Nova/Blade) behind a policy. Anyone who can write settings can change limits and copy across every tenant — treat it as admin-only and, for anything regulated, add an append-only setting_changes audit table.

Warm the cache on deploy. Add a post-deploy php artisan settings:warm step, or Cache::lock around the first rebuild so a fresh release doesn't stampede the DB.

Gotchas and Tips

  • Do not composer require it into Laravel. Solver conflicts are near-certain; --ignore-platform-reqs installs an untested dependency graph into production — not a mitigation. If you must reuse its logic, vendor a pinned fork and expose it behind one internal interface so no app code touches Smirik\ConfigBundle\*.
  • php artisan config:cache will not see DB values. Config files load before the DB guarantee exists. Keep DB-backed settings in the repository above, injected lazily — never read them inside config/*.php.
  • Merge semantics differ. The bundle's defaults-vs-overrides merge is not array_merge; a misunderstood precedence yields wrong runtime values, not an exception. Write a test that asserts each layer wins where you think it does.
  • Cache invalidation across servers is the failure mode. A rememberForever key that is only forgotten on one node means stale config on the others. Use a shared cache store, or version the key (settings:{env}:{version}) and bump the version on write.
  • Stale entries after deletion. Invalidate on deleted and saved in model events, not just in your update controller — anything bypassing the controller (tinker, a migration, an import) must still bust the cache.
  • Beware unbounded growth. Config tables accumulate dead keys. Add a settings:prune command that flags rows with no matching code default.
  • Debugging opacity in the original. Errors surface from inside a Symfony container/extension stack; there is no Laravel-style exception page pointing you at the offending config. This is the strongest argument for the native rebuild — you own the stack trace.
  • Provenance is the real risk. Zero stars, zero dependents, no visible maintainer activity, no SLA, licence unverified at assessment time. Treat the repo as sample code you adopt. Before any spike: last commit, licence file, test suite, CI, and how writes are authorised.
  • Extension points map cleanly. Its Twig helper → a Blade directive or settings() helper; its DI service → a container singleton; its compiler pass → a service provider register()/boot(). Nothing in the bundle is hard to reproduce.
  • Recommendation. Reimplement (roughly 3–5 engineer-days for a table, repository, cache, validation, and admin CRUD) or use a maintained Laravel package (spatie/laravel-settings). Revisit the Symfony bundle only if you are genuinely operating a Symfony application — there it is at least architecturally native, but you still inherit ownership of an unmaintained fork.
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core