abdelhamiderrahmouni / laravel-settings
Production ready, super simple settings management for your laravel app.
Package info
github.com/abdelhamiderrahmouni/laravel-settings
pkg:composer/abdelhamiderrahmouni/laravel-settings
Requires
- php: ^8.2|^8.3
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- rector/rector: ^2.3
README
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.