mesd/settings-bundle
MESD Settings Bundle adds flexible, database-backed application settings to Symfony projects. Define and organize typed settings, then read or update values through a simple service API with Doctrine integration.
Reality check first: mesd/settings-bundle is a Symfony bundle (last release March 2015, zero stars, zero dependents). It will not install into a Laravel application — it depends on symfony/framework-bundle, symfony/dependency-injection, and Doctrine ORM, none of which Laravel provides. A Laravel developer's "getting started" is therefore one of two paths:
composer require mesd/settings-bundle, register new MESD\SettingsBundle\SettingsBundle() in AppKernel, import the bundle's routing/config, then run the Doctrine schema update to create the settings table.Where to look first: clone the repo and read, in this order:
Resources/config/services.yml — shows the SettingsManager service and its dependencies (entity manager, entity class, cache).Entity/Settings.php (or Model/) — the schema: typically name, value, timestamps, maybe a scope/namespace column.DependencyInjection/Configuration.php — the knobs you can tune (entity class override, cache service).Twig/ extension — how settings reach templates (usually setting('key')).First use case: application-wide key/value configuration that an admin can edit at runtime without a deploy — site name, contact email, feature flags, API keys, maintenance-mode switches. That's the entire value proposition, and it's a 30-minute Eloquent port.
Pattern A — Symfony, native usage. Inject the manager and read/write:
$settings = $container->get('mesd_settings.manager');
$settings->set('site.name', 'Acme');
$settings->get('site.name', 'Default');
Persist through the entity manager, then flush. Flush batching matters — don't flush per set().
Pattern B — Laravel port (recommended). The bundle's model maps cleanly to:
// settings table: key (unique), value (text/json), type, group, updated_at
class Setting extends Model {
protected $fillable = ['key', 'value', 'type', 'group'];
protected $casts = ['value' => 'json']; // or cast per type
}
Back it with a repository that hydrates all settings into a single cached array on boot, so setting('site.name') is a memory lookup, not a query:
class SettingsRepository {
public function all(): array {
return Cache::rememberForever('settings.all', fn () =>
Setting::pluck('value', 'key')->all()
);
}
public function set(string $key, $value): void {
Setting::updateOrCreate(['key' => $key], ['value' => $value]);
Cache::forget('settings.all');
}
}
Bind a setting() helper or a Settings facade in a service provider. This mirrors the Symfony manager's API closely enough that tests port over.
Pattern C — Scoped settings. The bundle's typical extension point is per-user or per-tenant overrides. In Laravel, add a scope column (global, user:42, tenant:7) and resolve with a fallback chain: scope → global → hardcoded default. Cache per scope key, not globally.
Pattern D — Typed values. Store type (string|int|bool|json|array) alongside the value and cast on read, otherwise "false" from a form is truthy and your feature flag silently enables. This is the single most common bug in every settings-table implementation, including this one.
Integration tips:
get() never returns null in prod.config('...') for anything that must be known at boot (queue drivers, DB connections); use the settings table only for values that can safely resolve after the app is up.doctrine/orm version constraints before you write a line of code. Only viable on Symfony 2.x/3.x with an older Doctrine.config/settings.php. composer require will either fail on Symfony constraints or pull a parallel Symfony DI stack into your vendor dir. Don't.Settings entity uses annotations/XML mapping. If you port it, rewrite the schema by hand — do not copy the entity, since its mapping metadata and EntityManager interactions won't translate.set()/delete() methods, or use a model observer. Miss one write path and you ship stale settings.value is a text column and you store arrays with serialize()/json_encode(), be explicit about which. Mixed formats in one column is a debugging nightmare; add a type column or migrate to a JSON column.setting('key') returns the default when you know the row exists, check (a) the cache hasn't been forgotten, (b) the value isn't null vs 'null' vs empty string — the manager's get($key, $default) usually only falls back on null, not on ''.get($key, $default) signature. Both are exactly the API you want in a Laravel helper; copy the ergonomics, not the code.config, a settings table, or env vars). The bundle's data is just key/value — the export is trivial; the risk is forgetting keys that no one documented.How can I help you explore Laravel packages today?