Skip to content
Docs / Packages / Laravel Settings

Laravel Settings

Contents


Introduction

Laravel Settings provides a simple, typed, and cacheable way to store dynamic application configuration in your database. Rather than hardcoding values in .env files or static config/ files, Laravel Settings allows you to manage runtime configuration — such as feature flags, site preferences, and per-model options — from within your application itself.

The package supports both application-level settings (global configuration shared across your entire application) and model-level settings (per-model configuration, such as user preferences or team options). Settings are stored in the database, automatically cached for performance, and exposed through a clean, expressive API including a global helper, a facade, Blade directives, and support for dependency injection.

1// Store and retrieve a global application setting
2settings()->set('site.name', 'My Application');
3settings()->get('site.name');
4 
5// Store and retrieve a setting on any Eloquent model
6$user->settings()->set('ui.theme', 'dark');
7$user->settings()->get('ui.theme');
8 
9// Check whether a feature flag is enabled
10if (flag('billing.enabled')) {
11 // Charge the customer
12}

When To Use This Package

This package is designed for configuration that needs to change at runtime — without modifying environment variables or redeploying your application. Common use cases include:

Application-level settings:

  • Site name, logo URL, or branding values
  • Feature toggles controlled by an admin panel
  • Maintenance mode flags or runtime switches
  • Configurable thresholds or limits

Model-level settings:

  • User preferences such as notification settings, UI theme, or dashboard layout
  • Team or organisation-specific configuration
  • Per-project or per-resource metadata
  • Account-level feature overrides

When not to use this package:

You should continue using .env files and config/*.php for values that are environment-specific, secret, or required before the application fully boots:

Storage Use For
.env Database credentials, API keys, secrets, environment-specific infrastructure
config/*.php Static application configuration, values needed at boot time
egough/laravel-settings Runtime, database-driven configuration that changes without redeployment

Note: Settings stored in this package are loaded from the database (and cached). They are not suitable for values required during the service container boot cycle, such as queue connection credentials or logging configuration.


Installation

Install the package via Composer:

1composer require egough/laravel-settings:^1.0

Publishing Assets

Publish the configuration file:

1php artisan vendor:publish --tag=settings-config

Publish the migrations for application settings:

1php artisan vendor:publish --tag=settings-migrations

If you plan to use model-level settings, also publish the model settings migration:

1php artisan vendor:publish --tag=settings-model-migrations

Run your migrations:

1php artisan migrate

This will create the necessary database tables for storing your settings.


Configuration

After publishing, the configuration file will be located at config/settings.php. This file allows you to define default values for your settings, which act as fallbacks when a setting has not yet been stored in the database.

1return [
2 
3 'defaults' => [
4 'site.name' => 'Laravel App',
5 'site.logo' => null,
6 'billing.enabled' => false,
7 ],
8 
9];

Value Resolution Order

When you retrieve a setting, the package resolves the value in the following order:

  1. Database value — A value explicitly stored in the database takes highest priority.
  2. Config default — If no database value exists, the value defined in config/settings.php under defaults is used.
  3. Fallback argument — If neither of the above exists, the fallback you pass directly to get() is returned.

This means you can always provide a sensible default without ever throwing exceptions for missing values.


Application Settings

Application settings are global and accessible from anywhere in your application — controllers, jobs, services, commands, and views.

Setting a Value

Use the settings() helper to store a value:

1settings()->set('site.name', 'My Application');
2settings()->set('billing.enabled', true);
3settings()->set('features.max_uploads', 10);

You may also use dot notation to organise your settings into logical groups, which helps keep your keys readable and consistent.

Retrieving a Value

Retrieve a stored value using the get method:

1$name = settings()->get('site.name');

You may pass a default value as the second argument. This value is returned if no database value and no config default exists for the key:

1$theme = settings()->get('ui.theme', 'light');

Removing a Setting

To remove a setting from the database entirely:

1settings()->forget('site.name');

Once forgotten, subsequent calls to get will fall back to the config default or provided fallback.

Retrieving All Settings

To retrieve all stored application settings:

1$all = settings()->all();

This returns a collection of all settings currently stored in the database.


Model Settings

In addition to application-wide settings, you can attach settings to any Eloquent model. This is ideal for storing per-user preferences, per-team configuration, or any per-resource metadata.

Preparing Your Model

Add the HasSettings trait to any Eloquent model:

1use Egough\LaravelSettings\Traits\HasSettings;
2 
3class User extends Authenticatable
4{
5 use HasSettings;
6}

You can add this trait to as many models as you need:

1class Team extends Model
2{
3 use HasSettings;
4}
5 
6class Project extends Model
7{
8 use HasSettings;
9}

Note: Ensure you have published and run the model settings migration before using this feature.

Setting a Model Value

Access the model's settings via the settings() method:

1$user = User::find(1);
2 
3$user->settings()->set('ui.theme', 'dark');
4$user->settings()->set('notifications.email', true);
5$user->settings()->set('dashboard.layout', 'grid');

Settings are scoped to the individual model instance, so two different users will have completely independent settings.

1$alice = User::find(1);
2$bob = User::find(2);
3 
4$alice->settings()->set('ui.theme', 'dark');
5$bob->settings()->set('ui.theme', 'light');
6 
7$alice->settings()->get('ui.theme'); // 'dark'
8$bob->settings()->get('ui.theme'); // 'light'

Retrieving a Model Value

1$theme = $user->settings()->get('ui.theme');

You may provide a fallback as the second argument:

1$layout = $user->settings()->get('dashboard.layout', 'list');

Retrieving All Model Settings

Retrieve all settings stored for a given model instance:

1$preferences = $user->settings()->all();

Removing a Model Setting

Remove a specific setting from the model:

1$user->settings()->forget('ui.theme');

Setting Events

The package dispatches Laravel events when settings are created, updated, or deleted through a manager. Helpers, facades, model settings, and the settings:set Artisan command use these managers and emit the same events.

Register a listener in your application's service provider:

1use Egough\LaravelSettings\Events\SettingCreated;
2use Egough\LaravelSettings\Events\SettingDeleted;
3use Egough\LaravelSettings\Events\SettingUpdated;
4use Illuminate\Support\Facades\Event;
5 
6Event::listen(SettingUpdated::class, function (SettingUpdated $event): void {
7 if ($event->key === 'notifications.enabled') {
8 // $event->oldValue and $event->newValue contain decoded values.
9 // $event->model is the owner for model settings, or null globally.
10 }
11});
12 
13Event::listen(SettingCreated::class, function (SettingCreated $event): void {
14 // React to the first stored value for this key and scope.
15});
16 
17Event::listen(SettingDeleted::class, function (SettingDeleted $event): void {
18 // $event->oldValue contains the value removed from storage.
19});

Event Payloads

All event classes live in Egough\LaravelSettings\Events and expose public constructor properties:

Event Properties
SettingCreated string $key, mixed $newValue, ?Model $model = null
SettingUpdated string $key, mixed $oldValue, mixed $newValue, ?Model $model = null
SettingDeleted string $key, mixed $oldValue, ?Model $model = null

Model refers to Illuminate\Database\Eloquent\Model. For model settings, the event contains the owning model passed to for($model) or using HasSettings, rather than the internal settings row. Global events use null.

Change Detection

Events describe changes to stored overrides within the requested scope:

  • A missing stored key emits SettingCreated when written, even if its value matches a config default.
  • Existing keys emit SettingUpdated only when their decoded values differ using strict comparison (!==). For example, changing a decoded string '1' to integer 1 emits an update.
  • Equal decoded values emit nothing, even if the storage type changes. JSON objects are decoded to arrays before comparison.
  • Removing an existing key emits SettingDeleted. Removing a missing key emits nothing.
  • Stored null counts as an existing key. Config defaults and caller fallbacks do not count as stored values.

Managers read uncached repository snapshots before and after writes. This preserves compatibility with custom repositories and adds two uncached reads per write. Snapshot comparisons are per-call change detection, not an atomic audit trail under concurrent writes.

Transactions and Listeners

With the built-in database repositories, events are delivered after the settings storage transaction commits. They are discarded if that transaction rolls back. Without an active storage transaction, listeners run synchronously before set() or forget() returns.

For model settings, transaction timing follows the connection used by the settings table, which may differ from the owning model's connection.

Payload values are captured at write time. Multiple changes in a transaction produce separate events; they are not combined into one final change. A listener reading settings after commit sees the current stored value, which may differ from an earlier event's captured value.

The relevant cache is invalidated after successful writes and again immediately before event delivery, so listeners can read fresh settings even if the cache was repopulated during the transaction.

Failed writes do not emit events. Listener exceptions propagate to the caller; they do not undo a write that has already committed. Direct repository, Eloquent, and SQL writes do not emit these package events.

The implementation uses connection afterCommit() callbacks, available from Laravel 10.0, and does not require the ShouldDispatchAfterCommit interface introduced later in Laravel 10.

Custom Repositories

Existing SettingsRepository and ModelSettingsRepository interfaces are unchanged. Custom repositories receive immediate event delivery by default, including when an unrelated Laravel database transaction is active.

To defer delivery until your storage commits, additionally implement Egough\LaravelSettings\Contracts\DefersSettingEvents:

1public function afterCommit(callable $callback): void
2{
3 // Use the connection that stores your settings.
4 $connection = $this->connection;
5 
6 if ($connection->transactionLevel() > 0) {
7 $connection->afterCommit($callback);
8 } else {
9 $callback();
10 }
11}

The implementation must execute the callback immediately without an active transaction, defer it until successful commit otherwise, and discard it on rollback. Repositories using other storage must supply equivalent coordination for their own transactions.

Both managers accept an optional third constructor argument, events, implementing Illuminate\Contracts\Events\Dispatcher. Existing two-argument construction resolves Laravel's events service when delivering an event. The package service provider injects that service automatically.


Feature Flags

Feature flags are boolean settings used to toggle functionality on or off at runtime. They allow you to ship incomplete features safely, roll out functionality progressively, or let administrators control what's available in the application without code changes.

Checking a Flag

Use the global flag() helper:

1if (flag('billing.enabled')) {
2 // The billing feature is active
3}

You may provide a default fallback as the second argument:

1if (flag('beta.new_dashboard', false)) {
2 return view('dashboard.beta');
3}
4 
5return view('dashboard.stable');

Feature flags are backed by the same settings storage as regular settings, but are always cast to a boolean for convenience.

Using the Flag Facade

You may also use the Flag facade:

1use Flag;
2 
3if (Flag::enabled('billing.enabled')) {
4 // Feature is enabled
5}

Blade Directives

Laravel Settings provides a set of Blade directives so you can conditionally render content in your views based on settings and feature flags without cluttering your templates with verbose PHP.

@setting

Render a setting value directly in a template:

1<title>@setting('site.name')</title>
2 
3<meta name="description" content="@setting('site.description')">

@hasSetting

Conditionally render content if a setting exists and has a value:

1@hasSetting('site.announcement')
2 <div class="banner">
3 @setting('site.announcement')
4 </div>
5@endhasSetting

@hasFlag

Conditionally render content based on a feature flag:

1@hasFlag('billing.enabled')
2 <x-billing-panel />
3@endhasFlag
4 
5@hasFlag('beta.new_dashboard')
6 @include('partials.beta-notice')
7@endhasFlag

These directives keep your Blade templates expressive and easy to read, and they respect the same value resolution order as the PHP API.


Default Values

Default values provide a baseline for your settings before any values are saved to the database. They are defined in config/settings.php under the defaults key:

1'defaults' => [
2 'site.name' => 'Laravel App',
3 'site.locale' => 'en',
4 'billing.enabled' => false,
5 'ui.theme' => 'light',
6 'features.max_uploads' => 25,
7],

Defaults are returned transparently — calling settings()->get('site.name') will return 'Laravel App' even if the key has never been written to the database.

This means you can safely call get() throughout your application without worrying about null values for settings that haven't been explicitly configured yet.


Caching

Settings are automatically cached to avoid repeated database queries on every request. This means your application will only hit the database for settings once, and subsequent reads within the same request lifecycle are served from cache.

Clearing the Cache

If you update settings directly in the database, or need to force fresh values to be loaded, you can clear the settings cache using the provided Artisan command:

1php artisan settings:clear-cache

You may wish to call this command as part of your deployment pipeline if you manage settings via database seeding or migrations.

Note: When you set or forget a setting through the package's API (via the helper, facade, or model), the cache is automatically invalidated for you. Manual cache clearing is only necessary when you bypass the package's API and write directly to the database.


Artisan Commands

Laravel Settings includes a set of Artisan commands for managing settings from the command line, which is particularly useful in deployment scripts, seeding workflows, or quick debugging.

Retrieving a Setting

1php artisan settings:get site.name

Setting a Value

1php artisan settings:set site.name "My Application"
2php artisan settings:set billing.enabled true

Clearing the Cache

1php artisan settings:clear-cache

Dependency Injection

For applications that favour dependency injection over facades and global helpers, you may type-hint the SettingsManager class directly in any constructor or method resolved by the service container:

1use Egough\LaravelSettings\SettingsManager;
2 
3class SiteConfigurationController extends Controller
4{
5 public function __construct(
6 private readonly SettingsManager $settings,
7 ) {}
8 
9 public function show(): array
10 {
11 return [
12 'name' => $this->settings->get('site.name'),
13 'locale' => $this->settings->get('site.locale', 'en'),
14 ];
15 }
16 
17 public function update(Request $request): void
18 {
19 $this->settings->set('site.name', $request->input('name'));
20 }
21}

The SettingsManager is bound as a singleton in the service container, so the same instance — and therefore the same in-memory cache — is shared throughout the lifetime of a single request.


Testing

The package ships with a full test suite using an in-memory SQLite database, meaning no external database is required to run tests.

Running the Tests

Install the development dependencies:

1composer install

Run the test suite:

1composer test

Or run PHPUnit directly:

1vendor/bin/phpunit

Testing Your Own Application

When writing tests for code that reads or writes settings, you can use the settings() helper or the SettingsManager directly within your test to set up state:

1it('shows the correct site name in the header', function () {
2 settings()->set('site.name', 'Test App');
3 
4 $response = $this->get('/');
5 
6 $response->assertSee('Test App');
7});

For feature flag tests:

1it('shows the billing panel when billing is enabled', function () {
2 settings()->set('billing.enabled', true);
3 
4 $response = $this->actingAs($this->user)->get('/dashboard');
5 
6 $response->assertSeeLivewire('billing-panel');
7});
8 
9it('hides the billing panel when billing is disabled', function () {
10 settings()->set('billing.enabled', false);
11 
12 $response = $this->actingAs($this->user)->get('/dashboard');
13 
14 $response->assertDontSeeLivewire('billing-panel');
15});

Because settings are stored in the database, they will be reset between tests as long as you use the RefreshDatabase trait in your test cases.


Requirements

Requirement Version
PHP 8.2+
Laravel 10, 11, 12, or 13

License

Laravel Settings is open-sourced software licensed under the MIT license.