lexxpavlov/settingsbundle
SettingsBundle provides database-backed application settings management for PHP projects, letting you define, store, and retrieve configuration values through a simple service API. It supports framework integration, making runtime configuration easy to manage without editing files.
The package installs as a Symfony bundle, so treat it as drop-in service wiring around a Doctrine-backed setting table.
composer require lexxpavlov/settingsbundleLexxpavlov\SettingsBundle\LexxpavlovSettingsBundle to config/bundles.php (or AppKernel on older Symfony).php bin/console doctrine:schema:update --force in dev, or generate a migration and run it. Don't rely on schema:update in production.Lexxpavlov\SettingsBundle\Settings\SettingsManager. Inject it into a controller or service.public function __construct(private SettingsManager $settings) {}
$perPage = $this->settings->get('catalog.per_page', 20); // read with default
$this->settings->set('catalog.per_page', 50); // persist override
Where to look first:
SettingsManager — the public read/write API.Setting entity and its repository — how rows are persisted and scoped.config/packages/lexxpavlov_settings.yaml (default bundle config) — table name, owner class, type definitions.First realistic use case: replace the hard-coded items_per_page, maintenance_mode, or mail.from value in config/ with a persisted setting the admin can edit from a back-office form, without a redeploy.
Centralize key names. Don't scatter string keys through the codebase. Define them once (class constants, an enum, or a small settings catalog) and reference the constant everywhere; this is the cheapest way to prevent silent typos that fall back to defaults.
Namespace by domain. Use dotted keys grouped by area — catalog.per_page, billing.currency, mail.from — so admin screens can list and group settings by prefix rather than dumping a flat table.
Treat the DB as an override layer. Always pass a default on get(). The static config/ stays the source of truth for sane values; the settings table only records deviations. This makes a fresh install behave correctly before anyone touches the admin UI.
Scope per-owner when it makes sense. The bundle supports attaching a setting to an owner entity (e.g., User, Tenant). Use ownerless rows for global/site settings and owner-scoped rows for per-user preferences. Wrap the owner-aware read/write in a small service so callers never confuse the two.
Admin editing form. Build a controller that loads settings by group, renders a Symfony form, and writes back via SettingsManager. Batch the writes and flush once per request — avoid a set()/flush per field.
Cache the hot reads. If settings are read on every request, don't hit the DB for each one. Hydrate a plain settings object at kernel boot (or in a warmup cache) and read from that; invalidate on write. A Doctrine listener (postPersist/postUpdate on the Setting entity) is the natural hook for cache invalidation.
Expose a Twig function / view model. Templates shouldn't inject the manager. Register a Twig function like setting('catalog.per_page') backed by the cached reader, or pass values into the view from the controller.
Test with a real DB or a mocked manager. For unit tests, mock the manager interface. For integration tests, seed default settings via fixtures so the "default + override" behavior is covered.
composer why-not) before committing; you may need config-level backports or a fork for Symfony 6/7.get('feature.enabled') returns "0"/"1" rather than false/true.setting table must exist. Generate a migration in dev and ship it; don't let production depend on schema:update.How can I help you explore Laravel packages today?