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

Laravel Settings Laravel Package

ruangdeveloper/laravel-settings

A lightweight Laravel package for managing application settings stored in the database. Easily get and set configuration values with helper functions, caching support, and a simple API for organizing settings by group or key.

View on GitHub
Deep Wiki
Context7

Getting Started

What this package is: ruangdeveloper/laravel-settings is a database-backed key/value settings store for Laravel — an alternative to sprinkling config() files or a hand-rolled settings table. Values live in a table, are read through a facade/helper, and (in the v2 line) go through a cache layer so reads don't hit the DB on every request.

Install (3 commands):

composer require ruangdeveloper/laravel-settings
php artisan vendor:publish --provider="RuangDeveloper\LaravelSettings\LaravelSettingsServiceProvider"
php artisan migrate

If the provider isn't auto-discovered or the tag differs, check the package README for the exact publish tag — publish tags are the single most common source of "the table doesn't exist" errors with settings packages.

Where to look first, in this order:

  1. config/laravel-settings.php (or similarly named) — table name, cache store, cache TTL, cache key prefix, and the model class binding. This is where 90% of production surprises come from.
  2. The published migration — note the column types, especially whether value is text/json and whether there is a unique index on key. Multi-tenancy or grouping is not included out of the box; you'll add it yourself.
  3. The facade class — confirm the exact facade alias and helper function name for your version before copy-pasting snippets from blog posts.

First use case that pays for itself immediately: user-editable runtime settings for an admin panel — site name, support email, feature flags, maintenance-mode toggles, third-party API keys (encrypted at rest via a custom cast, not by the package). Anything an ops person should be able to change without a deploy is a good fit; anything that changes per-request is not.

Setting::set('site.name', 'Acme');
Setting::get('site.name', 'Default');

// or the helper, depending on version
setting('site.name');

Version note for v2.3.0: the release is a single compatibility PR adding Laravel 13 support. There are no breaking changes, no new features, no deprecations — the upgrade is composer update ruangdeveloper/laravel-settings and re-run your test suite. The only thing worth verifying is the illuminate/* constraint in composer.json: when maintainers "add" a framework version they sometimes bump the minimum as a side effect. If you're still on Laravel 11 or 12, confirm ^2.3 still resolves before bumping, and pin "ruangdeveloper/laravel-settings": "^2.3" if you intend to move to Laravel 13.

Implementation Patterns

Wrap it in your own repository, don't call the facade from controllers. The package API is a thin key/value layer; your domain isn't. A single App\Settings\SiteSettings class that exposes typed properties (public string $siteName, public bool $maintenanceMode) means you can swap the storage engine later, and it gives you one place to add validation, defaults, and casts.

final class SiteSettings
{
    public function __construct(private SettingStore $store) {}

    public function siteName(): string
    {
        return (string) $this->store->get('site.name', config('app.name'));
    }

    public function maintenanceMode(): bool
    {
        return (bool) $this->store->get('site.maintenance', false);
    }
}

Namespace your keys. Adopt a dotted convention (billing.currency, mail.from_address, features.new_checkout) from day one. The package stores keys as flat strings; a prefix convention is what makes a later export, audit, or "reset group" feature tractable.

Seed defaults idempotently, in a seeder — not in a migration. Migrations run once; settings should be re-assertable. Use firstOrCreate-style semantics so re-running the seeder on an existing environment doesn't clobber values an admin has already edited:

foreach ($defaults as $key => $value) {
    Setting::has($key) || Setting::set($key, $value);
}

Cache-invalidate at the write, and again at deploy. The package's caching is the reason reads are cheap. That means every write path must go through the package (or your repository) — a direct DB::table('settings')->update(...) in a hotfix script will leave stale values in Redis until TTL expiry, which can be hours. If you must write raw, follow it with the package's cache-clear call (or Cache::forget() on the package's key prefix). Add a cache clear to your deploy script as a belt-and-braces step.

Prefer a typed cast over stringly-typed values. Booleans stored as "0"/"1" and arrays stored as JSON both round-trip badly when a human edits the row in a DB client. Either use the package's type/cast option if it exposes one, or normalize in your repository: JSON-encode on write, json_decode(..., true) on read, and treat null as "not set" rather than false.

Read-through config fallback is the pattern that keeps you honest: settings should override a sane default that lives in config/, never be the only source of a value. That way a fresh clone, a broken settings table, or an empty cache still boots.

For tests: the settings table is real state. Use RefreshDatabase, and set the package's cache store to array (or null) in phpunit.xml — otherwise a cached maintenance_mode = true leaks from one test into the next and you spend an afternoon on a phantom 503.

In Blade / Inertia, resolve settings once per request (a view composer or a shared Inertia prop) rather than calling the facade repeatedly inside loops.

Gotchas and Tips

  • The cache is the sharp edge. Multi-server deploys, queue workers, and Octane workers each hold their own view of the world. Use a shared cache store (Redis/Memcached) in any environment with more than one process. array and file stores will silently diverge; array in production is a bug, not a performance choice.
  • config:cache does not help you here. Settings are runtime DB state, not configuration. Don't try to bake them into config files for "speed" — you'll reintroduce deploys for things that were supposed to be runtime-editable, and you'll break config:cache ordering.
  • Laravel 13 support means check your constraint, not just your code. v2.3.0 is a pure compatibility release. Before upgrading, run composer why-not ruangdeveloper/laravel-settings 2.3.0 — if a transitive illuminate/support pin is holding you back, that's a Composer problem, not a package problem.
  • Re-publish and diff the migration if you're coming from v1 or early v2. Migration stubs change shape between majors; a missing unique index on key turns set() into silent duplicate rows and makes get() non-deterministic. Check with SHOW INDEX FROM settings after upgrading.
  • No first-class multi-tenancy. Per-tenant settings require you to add a tenant_id (or group) column, override the model binding in config, and add a global scope. Budget for this; don't discover it in month three.
  • Concurrency on write. Two admins saving the same form produce last-write-wins. If that matters, wrap the write in a transaction with a row lock, or version the payload. The package won't do it for you.
  • Secrets don't belong here unencrypted. API keys in a text column are readable by anyone with DB access, and by every php artisan db:dump in your CI logs. Add an encrypted cast on the model (or store a reference to your secrets manager) before someone commits a production dump.
  • Extension points worth knowing: the model class and table name in config (override both to add columns/casts), the cache store and key prefix in config, and the service provider binding if you want to decorate the store with logging or an audit trail. These three cover almost every legitimate customization; if you find yourself editing vendor/, stop.
  • Debugging tip: when a value "won't change," check in this order — (1) is it actually in the DB (SELECT * FROM settings WHERE key = ...), (2) what cache store is active (php artisan tinker → Cache::getStore()), (3) is a config fallback masking a null read. That's 95% of reported issues with this class of package.
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.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
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