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

arcanedev/laravel-settings

Laravel Settings provides a simple, persistent way to manage application settings in Laravel. Store and retrieve settings with database or config drivers, cache support, and an easy-to-use API for global configuration values.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require arcanedev/laravel-settings

php artisan vendor:publish --provider="Arcanedev\Settings\SettingsServiceProvider" --tag="laravel-settings:config"
php artisan vendor:publish --provider="Arcanedev\Settings\SettingsServiceProvider" --tag="laravel-settings:migrations"
php artisan migrate

The service provider is auto-discovered on modern Laravel; only older skeletons need the config/app.php entry. Publishing without --tag dumps every stub — always use the tags so you don't overwrite the migration on the next publish.

Where to look first, in this order:

  1. The published config file — this is where you pick the driver (database, json, redis, plus an in-memory driver used for tests) and the table/path/hash it writes to.
  2. vendor/arcanedev/laravel-settings/src/Repositories/ — read the abstract repository and the class for your driver. It is only a few dozen lines each and it answers the one question that matters before you build anything on top: does set() write immediately, or does it stage in memory until an explicit save()?
  3. The facade Arcanedev\Settings\Facades\Settings (a setting() helper is also registered).

First use case — the one this package exists for: values a non-developer needs to change without a deploy.

use Arcanedev\Settings\Facades\Settings;

Settings::get('site.name', config('app.name'));   // read with fallback
Settings::set('site.name', 'Acme');               // write
Settings::has('site.name');
Settings::forget('site.name');
Settings::all();                                  // array of everything

If the value belongs in version control and changes with a code review, it belongs in config/. If it changes because marketing asked, it belongs here.

Implementation Patterns

Always supply a default, and make the default the config() value. This lets a fresh install boot before anything is stored, and gives you a migration path from env-driven config to DB-driven settings.

$from = Settings::get('mail.from.address', config('mail.from.address'));

Group related keys with dot notation and write the group once.

Settings::set([
    'site.name'    => $request->input('site_name'),
    'site.tagline' => $request->input('tagline'),
    'site.locale'  => $request->input('locale'),
]);

Reading Settings::get('site.tagline') after storing a nested array works, but be consistent: either always write leaves (site.tagline) or always write the parent array (site). Mixing the two is how you get silent overwrites.

Admin update flow — treat it like any other write endpoint:

public function update(UpdateSiteSettingsRequest $request)
{
    $data = $request->validated();          // FormRequest whitelist, not $request->all()

    DB::transaction(fn () => Settings::set($data));
    Cache::forget('app.settings');

    return back()->with('status', 'Settings saved.');
}

Never map request input straight into Settings::set(); the package has no schema and will happily persist any key a user posts.

Wrap reads in a tiny cached accessor. The repository is loaded on first access, but the moment settings appear in a Blade layout or a loop you want one read per request (or per cache window), plus a place to put type coercion:

final class AppSettings
{
    public function all(): array
    {
        return Cache::rememberForever('app.settings', fn () => Settings::all());
    }

    public function bool(string $key, bool $default = false): bool
    {
        return filter_var($this->all()[$key] ?? $default, FILTER_VALIDATE_BOOL);
    }
}

Inject AppSettings rather than the facade so you can swap the implementation later (see Gotchas).

Bridge to config() at boot when a third-party package only reads config. In a service provider's boot():

config(['services.mailgun.domain' => Settings::get('mailgun.domain', config('services.mailgun.domain'))]);

This is request-time only and is not baked into config:cache — which is exactly what you want, but also means it must run on every request (including queue workers).

Share with views once: a view composer that injects AppSettings::all() into your layout beats calling setting() from partials.

Testing: point the driver at the in-memory/array implementation in phpunit.xml so tests don't need a settings table; use the database driver plus RefreshDatabase only in the tests that specifically cover persistence.

Multi-tenant: the package has no scoping. Prefix keys (tenant.{id}.site.name) or use a per-tenant connection in the database driver config; don't rely on separate Settings instances.

Gotchas and Tips

  • Verify the persistence semantics before writing a UI. If the repository stages values, a set() without save() survives the request but never hits storage — and nothing errors. Write one tinker test (Settings::set(...), then Settings::all() in a fresh process) on day one.
  • Values round-trip through a value column. Booleans and integers frequently come back as "1" / "0". Cast at the boundary ((bool), (int), filter_var), never inside Blade.
  • flush() is a loaded gun. It wipes every key for the configured driver. Don't expose it in an admin route, and if you must, gate it behind app()->isLocal() plus a confirmation step.
  • JSON driver: single-server only, needs write permission on storage/app, breaks on read-only filesystems (Vapor, some container images), and read-modify-write on the whole file races under concurrency. Fine for a single VPS; wrong for a load-balanced app.
  • Redis driver: lives in a hash, so php artisan cache:clear will not clear it, and it inherits your Redis persistence settings. Know whether you've configured Redis as cache or as store — flushing the wrong one hurts.
  • Don't read settings in register(), and guard boot-time reads on fresh installs. On the very first deploy (or in CI before migrate) the table may not exist and the provider throws. Wrap the read in a Schema::hasTable() check or a try/catch that falls back to config.
  • Config caching does not cache settings. They're read at runtime, which is the point — but it also means a config:cache deploy won't reveal a broken driver. Smoke-test the settings screen after deploy.
  • Long-lived processes: queue workers and Octane hold your cached copy. If you add a rememberForever layer, invalidate on write, and remember queue:restart after changing anything that isn't cache-backed.
  • Dot-key collisions: setting mail as an array after mail.from exists (or vice versa) silently drops keys. Pick leaf-level or group-level writes and stick to it.
  • No audit trail, no validation, no types. If settings gate behavior (feature flags, payment toggles), add a model observer or a listener on save that logs who changed what. This package is a key-value store, not a settings framework.
  • Custom drivers: the manager follows Laravel's Manager pattern, so you can register your own repository via extend(). Useful if you outgrow a driver — but the simpler escape hatch is a thin AppSettings interface in your own code, which also lets you migrate to spatie/laravel-settings (typed settings classes, validation, casts) later without touching call sites.
  • Maintenance risk: 73 stars, a single maintainer, last release March 2024. Pin the version in composer.json, keep the facade calls confined to one wrapper class, and treat the repository source as something you may need to fork. For anything long-lived and business-critical, weigh spatie/laravel-settings or a plain settings table with a model — the amount of code you'd write is small, and the package's main value is the driver abstraction, not the API.
  • Debugging: Settings::all() in php artisan tinker tells you instantly whether the problem is storage, caching, or your admin form. Check storage/logs/laravel.log for driver-level exceptions (permissions, connection, missing table) — the facade call itself rarely throws a useful message.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings