oneguard/dynamic-configuration-bundle
OneGuard Dynamic Configuration Bundle provides runtime configuration management for Symfony applications, letting settings be defined, overridden, and persisted without code changes. Ideal for feature flags, environment-specific values, and admin-editable options.
composer.json will pull symfony/framework-bundle (and almost certainly Doctrine). Laravel does not have a bundle kernel, so composer require oneguard/dynamic-configuration-bundle gives you the code but no wiring — there is no config/app.php entry to add and no bundles.php to register it in. The package is also archived, with 1 star and 0 dependents, so treat it as a reference implementation rather than a dependency.README.md — the expected table schema/settings keys.DependencyInjection/*Extension.php — how config is read and which parameters are set.Resources/config/services.* — the repository/service that actually queries the database.// migration
Schema::create('settings', function (Blueprint $t) {
$t->string('key')->primary();
$t->text('value')->nullable();
$t->string('type')->default('string'); // string|int|bool|json
$t->timestamps();
});
class SettingsRepository
{
public function __construct(private Cache $cache, private Connection $db) {}
public function all(): array
{
return $this->cache->rememberForever('settings.all', function () {
return $this->db->table('settings')->get()
->mapWithKeys(fn ($r) => [$r->key => $this->cast($r)])
->all();
});
}
public function flush(): void { $this->cache->forget('settings.all'); }
private function cast(object $row): mixed
{
return match ($row->type) {
'int' => (int) $row->value,
'bool' => filter_var($row->value, FILTER_VALIDATE_BOOL),
'json' => json_decode($row->value, true),
default => $row->value,
};
}
}
class SettingsServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(SettingsRepository::class);
}
public function boot(): void
{
try {
if (Schema::hasTable('settings')) {
config(['settings' => app(SettingsRepository::class)->all()]);
}
} catch (\Throwable) {
// fresh install / DB unavailable — skip
}
}
}
That mirrors what the bundle does (read rows → deserialize → merge into a parameter bag) using Laravel’s config repository and cache.
match-based casting, and config(['settings' => ...]) (or a dedicated dynamic namespace so it doesn’t collide with app.*).Cache::rememberForever and clear on saved/deleted via an observer. This reproduces the bundle’s cache-warmup concept without touching the config cache.boot(), so a cold DB doesn’t fail the whole app.| Symfony bundle concept | Laravel equivalent |
|---|---|
Bundle Extension / prependExtension |
ServiceProvider register() + mergeConfigFrom() |
| Compiler pass | $this->app->extend() / contextual binding |
| Parameter bag | config() repository |
| Cache warmer | Cache::remember + observer flush |
Settings::set() that writes the row, casts by type, flushes the cache, and optionally calls a reload() on the repository singleton. This is the one place to centralize all invalidation.php artisan config:cache will bite you. Files in config/ are evaluated during warmup — never query the DB from inside a config file (the DB may not exist yet, and the values get frozen into the cached config). Load dynamic settings at boot or lazily, not in config/*.php.boot() runs on every request. Guard Schema::hasTable('settings') with a cached boolean, and always memoize repository reads. On Octane, that guard result and the singleton both persist across requests.php artisan octane:reload (or a worker restart). If changes must be live, read the cached repository per request instead of snapshotting into config().boot() runs before the settings table exists. Wrap in try/catch and short-circuit; don’t let a missing table take down php artisan migrate or an empty CI database.phpunit.xml, use RefreshDatabase, and assert on the repository return value — not on global config() mutation, which leaks between tests.cache:clear after changes, and kernel boot ordering. Copy the typed value transformer (string|int|bool|json) and the cache key/version — those are the genuinely reusable bits.flush()/reload() method for the settings-save flow, and a type-cast strategy so adding float, datetime, or array values is a one-line change rather than a schema migration.How can I help you explore Laravel packages today?