laravel-settings maintained by abdelhamiderrahmouni
Laravel Settings
Production-ready, super-simple settings management for Laravel applications.
Store settings in the database with full support for per-user scoping, typed values, caching, and your choice of two definition styles:
- Enum approach — backed string enums implementing
SettingDefinition. Best when you want exhaustive compile-time keys and match-expression defaults. - Data Object approach — plain PHP classes extending
SettingsData. Best when you want IDE-typed property access and group-level read/write in one call.
Both approaches share the same database table, cache layer, and SettingsManager.
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13
Installation
composer require abdelhamiderrahmouni/laravel-settings
Publish and run the migration:
php artisan vendor:publish --tag=settings-migrations
php artisan migrate
Configuration
Publish the config file to customise the cache driver, TTL, cache key prefix, and table name:
php artisan vendor:publish --tag=settings-config
// config/settings.php
return [
'cache' => [
'driver' => env('SETTINGS_CACHE_DRIVER', 'file'),
'ttl' => env('SETTINGS_CACHE_TTL', 3600),
'prefix' => env('SETTINGS_CACHE_PREFIX', 'settings'),
],
'table' => env('SETTINGS_TABLE', 'settings'),
];
Approach 1 — Enum
Defining an enum
php artisan settings:make GeneralSettings
This generates app/Settings/GeneralSettings.php. Fill in your cases:
namespace App\Settings;
use Settings\Contracts\SettingDefinition;
enum GeneralSettings: string implements SettingDefinition
{
case SiteName = 'site_name';
case MaintenanceMode = 'maintenance_mode';
case MaxUploadSize = 'max_upload_size';
public function group(): string
{
return 'general';
}
public function type(): string
{
return match ($this) {
self::SiteName => 'string',
self::MaintenanceMode => 'boolean',
self::MaxUploadSize => 'integer',
};
}
public function default(): mixed
{
return match ($this) {
self::SiteName => 'My App',
self::MaintenanceMode => false,
self::MaxUploadSize => 10,
};
}
public function label(): string
{
return match ($this) {
self::SiteName => 'Site Name',
self::MaintenanceMode => 'Maintenance Mode',
self::MaxUploadSize => 'Max Upload Size (MB)',
};
}
}
Supported types: string, integer, boolean, float, array, json.
Reading and writing
use App\Settings\GeneralSettings;
use Settings\Facades\Settings;
// Read — returns the stored value cast to the declared type
Settings::get(GeneralSettings::SiteName);
// Read with a fallback (takes precedence over the enum default)
Settings::get(GeneralSettings::SiteName, 'Fallback');
// Write
Settings::set(GeneralSettings::SiteName, 'My App');
Settings::set(GeneralSettings::MaintenanceMode, true);
// Delete — subsequent get() calls return the enum default
Settings::forget(GeneralSettings::SiteName);
Per-user scoping
Settings::for($user)->get(GeneralSettings::SiteName);
Settings::for($user)->set(GeneralSettings::SiteName, 'Their App');
Settings::for($user)->forget(GeneralSettings::SiteName);
Approach 2 — Data Object
Data Objects give you a fully-typed PHP class where each property is a setting. The IDE knows the exact type of every value. You read and write a whole group at once using fill() and save().
Defining a Data Object
php artisan settings:make GeneralSettings --data
This generates app/Settings/GeneralSettings.php:
namespace App\Settings;
use Settings\SettingsData;
class GeneralSettings extends SettingsData
{
public function __construct(
public string $appName = 'My App',
public bool $maintenanceMode = false,
public int $maxUploadSize = 10,
public float $storageQuota = 5.0,
public array $allowedDomains = [],
) {}
public function group(): string
{
return 'general';
}
}
- Property types map directly to cast types — no separate
type()method needed. - Constructor defaults are the fallback values when no DB record exists.
- The
group()method maps to thegroupcolumn in the settings table. - camelCase property names are automatically stored as
snake_casein the database (e.g.$appName→app_name).
Reading settings
use App\Settings\GeneralSettings;
use Settings\Facades\Settings;
$settings = Settings::fill(GeneralSettings::class);
// Each property is typed — full IDE autocomplete
echo $settings->appName; // string
echo $settings->maintenanceMode; // bool
echo $settings->maxUploadSize; // int
fill() fetches all stored values for the group in a single query, falls back to constructor defaults for any that have no record, and returns a hydrated GeneralSettings instance.
Writing settings
$settings = Settings::fill(GeneralSettings::class);
$settings->appName = 'New Name';
$settings->maintenanceMode = true;
Settings::save($settings);
save() uses dirty tracking — only properties whose values changed since fill() was called are written to the database. Unmodified properties are not touched.
Per-user scoping
// Read per-user
$settings = Settings::for($user)->fill(GeneralSettings::class);
// Write per-user (only dirty properties saved)
$settings->appName = 'Their App';
Settings::for($user)->save($settings);
Global and per-user records are stored independently.
Choosing an approach
| Enum | Data Object | |
|---|---|---|
| Typed return values | Cast by type string | Native PHP property types |
| IDE autocomplete on values | Requires docblocks | Built-in via typed properties |
| Read a single setting | Settings::get() |
Settings::fill() then access property |
| Write a single setting | Settings::set() |
Mutate property + Settings::save() |
| Batch read a group | N/A | Settings::fill() — one query |
| Exhaustive compile-time keys | Yes (enum cases) | No |
| Labels for UI display | Yes (label() method) |
No (add your own if needed) |
| Generator command | settings:make Name |
settings:make Name --data |
Both approaches coexist in the same application. Use enums for granular, individually-accessed settings and Data Objects for grouped settings you want to work with as a typed unit.
Dependency injection
SettingsManager is bound as a singleton and can be injected directly:
use Settings\SettingsManager;
class UpdateSettingsAction
{
public function __construct(private readonly SettingsManager $settings) {}
public function handle(): void
{
// Enum approach
$this->settings->set(GeneralSettings::SiteName, 'My App');
// Data Object approach
$data = $this->settings->fill(GeneralSettings::class);
$data->appName = 'My App';
$this->settings->save($data);
}
}
Caching
Settings are cached per-key after the first read. The cache is automatically invalidated on every set(), forget(), and save() call — no manual cache management needed.
Cache keys follow the format {prefix}:{group}.{name} for global settings and {prefix}:{group}.{name}:{user_id} for per-user settings.
Customising stubs
To customise the templates used by settings:make, publish the stubs:
php artisan vendor:publish --tag=settings-stubs
This copies both stubs/settings.stub (enum) and stubs/settings-data.stub (Data Object) into your project root. The settings:make command will prefer your published stubs automatically.
Testing
composer test
License
MIT — see LICENSE for details.