damodar-bhattarai/settings
A Laravel settings package for storing and managing application configuration in the database. It offers a simple API to get, set, and forget settings, with caching support and easy integration into any Laravel application.
Install and publish
composer require damodar-bhattarai/settings
php artisan vendor:publish --provider="DamodarBhattarai\Settings\SettingsServiceProvider"
php artisan migrate
Publishing gives you the settings migration (usually a settings table with key / value columns). Check config/settings.php after publishing — this is where cache key, TTL, and table name are typically configured.
Where to look first
config/settings.php — cache key, TTL, table name, and any helper registration.First use case
Seed a few defaults (site name, logo URL, support email) via a seeder, then read them anywhere:
// helper
$siteName = settings('site_name', 'My App');
// facade
use DamodarBhattarai\Settings\Facades\Settings;
$logo = Settings::get('logo_url');
Render it in Blade:
{{ settings('site_name') }}
That is the whole point of the package: one round trip to the DB on the first read, then everything served from cache.
Read-with-default everywhere. Treat the helper's second argument as a required discipline. Configuration keys drift, admins delete rows, and a missing setting should never crash a Blade view.
settings('contact.phone', config('app.fallback_phone'));
Group settings logically with dotted keys. Store contact.email, contact.phone, branding.logo, branding.favicon. This makes it easy to fetch a whole group by prefix if the package supports it, and easier to reason about in an admin form.
Admin write path. Wrap updates in a form request and update one key at a time so the cache invalidates per key:
foreach ($request->validated('settings') as $key => $value) {
Settings::set($key, $value);
}
If you have many keys, batch them if the package exposes a setMany; otherwise clear the cache once after the loop rather than relying on per-key invalidation.
Bypassing the cache when you need truth. The differentiator this package advertises is "fresh values from the database." Use that sparingly — typically only in a settings admin screen showing the persisted value, or in a queued job where you suspect the cache is stale:
$fresh = Settings::fresh('site_name');
Never use fresh() inside a hot loop or a per-request path — you defeat the reason you installed the package.
Bootstrap fallbacks via seeder. Ship a SettingsSeeder that inserts default rows for every key your app references. This makes a fresh install deterministic and turns "undefined setting" from a runtime bug into a missing-seed error you catch in CI.
Config-driven defaults. Keep a config/settings.php defaults array that mirrors your seeded keys. Read order becomes: DB value → config default → hard-coded fallback. This keeps tests fast — you can swap defaults without touching the DB.
Blade directive for the layout. Most teams end up with a @setting('site_name') directive or a view composer that injects the common keys (site_name, logo_url, contact.email) into every layout. Do this once in a AppServiceProvider, not per view.
Multi-brand / multi-tenant. If you run more than one brand, the single global settings table becomes a bottleneck. Either prefix keys per tenant (tenant_42.site_name) or fork the package to scope by a tenant column — the cache layer almost certainly uses a single key, so scoping requires a cache-key strategy too. Decide this before you have 200 keys.
Cache invalidation is the whole game. The package caches everything; if Settings::set() does not Cache::forget() the relevant key (or tag), you will ship a bug where the admin changes the logo and the customer sees the old one for hours. Verify on first install:
Settings::set('site_name', 'New');
dump(settings('site_name')); // must be "New", not the cached old value
If it fails, clear manually in your controller or file an issue.
Deploy-time cache persistence. On shared Redis/Memcached across deploys, settings caches survive migrations. If you change the schema or rename keys, add a deploy step:
php artisan cache:forget settings # or your configured cache key
php artisan config:clear
Otherwise staging/prod will keep serving pre-rename keys and you will chase phantom nulls.
null vs missing. A setting stored as the empty string is different from a key that has never been set. settings('x') returns null for both unless the package distinguishes them. Be explicit in the admin form: save '' for "clear", and use the default argument as the "never configured" path.
String-only storage bites structured data. Logo URLs and phone numbers are fine as strings. Anything richer (arrays, feature flags, opening hours) should be JSON-encoded at the boundary and decoded on read, or you will store "[object Object]" in production. Consider whether the package auto-casts or leaves you to json_encode/json_decode manually — usually it's manual.
Type coercion surprises. settings('features.enabled') returns the string "false", which is truthy. Cast explicitly:
$enabled = filter_var(settings('features.enabled', false), FILTER_VALIDATE_BOOL);
Testing hygiene. The cache persists between tests and will make a settings test pass locally and fail in CI (or vice versa). Add Cache::flush() in setUp() of any test that touches settings, or use RefreshDatabase plus a cache array driver in phpunit.xml.
Don't leak the settings table. Settings often hold secrets (SMTP passwords, API keys). If the package does not support encryption, do not store secrets in it — put them in .env or Laravel's encrypted cast. Expose only the "safe" keys to the admin UI.
Key naming consistency. Dots for grouping, snake_case for segments. Mixed SiteName / site_name / site-name will fragment your cache and your admin form. Enforce it with a validation rule on the keys you accept.
Extension points to know. Look for: a custom SettingsStore contract, the ability to swap the cache repository, and any published migration you can edit to add a group or type column. Adding type lets you auto-cast on read, which is usually the highest-leverage change once you exceed ~20 keys.
When to outgrow it. If you find yourself building a permissions layer for who can edit which key, a versioned audit trail of setting changes, or per-tenant resolution — that is a sign to reach for spatie/laravel-settings with typed setting classes, or a bespoke admin module. This package is a lean key/value cache; it is excellent at that and will fight you on everything else.
How can I help you explore Laravel packages today?