# Laravel Settings Laravel Settings is a PHP package by Spatie that provides strongly typed application settings stored in a repository (database or Redis). It allows you to define settings as PHP classes with typed public properties, automatically handles serialization/deserialization, and integrates seamlessly with Laravel's dependency injection container. The package supports migrations for settings, type casting for complex types (DateTime, Enums, Data Transfer Objects), property encryption, property locking to prevent updates, caching for performance, and auto-discovery of settings classes. Settings classes can be injected anywhere in your Laravel application and updated with a simple save() call. ## Creating a Settings Class Settings classes extend the base `Settings` class and define typed public properties. The `group()` method returns a unique identifier for the settings group. ```php $settings->site_name, 'site_active' => $settings->site_active, 'timezone' => $settings->timezone, ]); } public function update(Request $request, GeneralSettings $settings) { $settings->site_name = $request->input('site_name'); $settings->site_active = $request->boolean('site_active'); $settings->timezone = $request->input('timezone'); $settings->save(); return redirect()->back()->with('success', 'Settings updated!'); } } ``` ## Creating Settings Migrations Settings migrations define default values for properties. Create migrations using artisan and use the migrator to add, rename, update, or delete properties. ```php migrator->add('general.site_name', 'My Application'); $this->migrator->add('general.site_active', true); $this->migrator->add('general.timezone', 'UTC'); } public function down(): void { $this->migrator->delete('general.site_name'); $this->migrator->delete('general.site_active'); $this->migrator->delete('general.timezone'); } }; ``` ## Migration Operations with SettingsBlueprint Use `inGroup()` for cleaner migrations when working with multiple properties in the same group. ```php migrator->inGroup('general', function (SettingsBlueprint $blueprint): void { // Add properties $blueprint->add('site_name', 'My Site'); $blueprint->add('maintenance_mode', false); // Rename a property $blueprint->rename('old_property', 'new_property'); // Update property value with closure $blueprint->update('site_name', fn(string $name) => strtoupper($name)); // Delete property $blueprint->delete('deprecated_setting'); }); } }; ``` ## Type Casting with DateTime The package automatically casts DateTime properties using the built-in DateTimeInterfaceCast. Works with DateTime, DateTimeImmutable, Carbon, and CarbonImmutable. ```php migrator->add('event.event_start', '2024-01-01 10:00:00'); $this->migrator->add('event.event_end', null); $this->migrator->add('event.created_at', now()->toIso8601String()); } }; ``` ## Enum Casting PHP native enums are automatically cast using the EnumCast for both backed and unit enums. ```php migrator->add('appearance.theme', 'light'); $this->migrator->add('appearance.notification_frequency', 'Daily'); } }; ``` ## Custom Local Casts Define custom casts for specific properties using the `casts()` method in your settings class. ```php DateTimeInterfaceCast::class.':'.DateTime::class, // Or instantiate cast directly for additional arguments 'end_date' => new DateTimeInterfaceCast(DateTime::class), ]; } } ``` ## Spatie Laravel Data Integration Cast properties to Data objects from spatie/laravel-data package using the DataCast. ```php migrator->add('company.name', 'Acme Inc'); $this->migrator->add('company.headquarters', [ 'street' => '123 Main St', 'city' => 'New York', 'country' => 'USA', 'postal_code' => '10001', ]); } }; ``` ## Encrypting Sensitive Properties Encrypt sensitive properties like API keys by specifying them in the `encrypted()` method. ```php migrator->add('api.api_endpoint', 'https://api.example.com'); $this->migrator->addEncrypted('api.api_key', 'sk_live_xxxx'); $this->migrator->addEncrypted('api.api_secret', 'secret_xxxx'); } }; ``` ## Locking Properties Lock properties to prevent them from being updated, useful for read-only settings or admin-controlled values. ```php lock('site_name'); $settings->lock('timezone', 'maintenance_mode'); // Check if locked if ($settings->isLocked('site_name')) { // Property cannot be modified } // Get all locked properties $locked = $settings->getLockedProperties(); // ['site_name', 'timezone', 'maintenance_mode'] // Unlock properties $settings->unlock('site_name'); // Check if unlocked if ($settings->isUnlocked('site_name')) { // Property can be modified } return response()->json(['locked' => $settings->getLockedProperties()]); } } ``` ## Specifying Custom Repository Use different repositories (database or redis) for different settings classes. ```php migrator->repository('redis'); $this->migrator->add('cache.ttl', 3600); $this->migrator->add('cache.excluded_paths', ['/admin', '/api/health']); } }; ``` ## Faking Settings in Tests Use the fake() method to create test instances with custom values without database interaction. ```php 'Test Site', 'site_active' => false, 'timezone' => 'America/New_York', ]); $settings = app(GeneralSettings::class); $this->assertEquals('Test Site', $settings->site_name); $this->assertFalse($settings->site_active); } public function test_partial_fake_loads_missing_from_repository() { // Only fake some properties, others loaded from repository GeneralSettings::fake([ 'site_name' => 'Partial Fake', ]); $settings = app(GeneralSettings::class); $this->assertEquals('Partial Fake', $settings->site_name); // site_active loaded from actual repository } public function test_strict_fake_throws_on_missing_properties() { $this->expectException(\Spatie\LaravelSettings\Exceptions\MissingSettings::class); // Pass false to disable loading missing values GeneralSettings::fake([ 'site_name' => 'Strict Fake', ], false); } } ``` ## Listening to Settings Events Subscribe to events for loading and saving settings to add custom logic. ```php settings; $wasFromCache = $event->loadedFromCache; logger()->info('Settings loaded', [ 'class' => get_class($settings), 'from_cache' => $wasFromCache, ]); } } class SavingSettingsListener { public function handle(SavingSettings $event): void { $settings = $event->settings; $newValues = $event->properties; $originalValues = $event->originalValues; // Audit changes $changes = $newValues->diffAssoc($originalValues ?? collect()); logger()->info('Settings saving', [ 'class' => get_class($settings), 'changes' => $changes->toArray(), ]); } } // Register in EventServiceProvider protected $listen = [ SettingsLoaded::class => [SettingsLoadedListener::class], SavingSettings::class => [SavingSettingsListener::class], SettingsSaved::class => [SettingsSavedListener::class], ]; ``` ## Settings as JSON Response Settings classes implement Arrayable, Jsonable, and Responsable interfaces for easy API responses. ```php toArray(); // ['site_name' => 'My Site', 'site_active' => true, 'timezone' => 'UTC'] // Convert to JSON string $json = $settings->toJson(); // '{"site_name":"My Site","site_active":true,"timezone":"UTC"}' // Convert to Collection $collection = $settings->toCollection(); return response()->json([ 'settings' => $array, 'keys' => $collection->keys(), ]); } } ``` ## Refreshing Settings from Repository Reload settings from the repository to get fresh values after external changes. ```php site_name; // External process updates the database directly... // Refresh to get new values from repository $settings->refresh(); // Now has fresh values $newName = $settings->site_name; return response()->json([ 'old' => $oldName, 'new' => $newName, ]); } } ``` ## Implementing Custom SettingsCast Create custom casts by implementing the SettingsCast interface for complex types. ```php currency = $currency; } public function get($payload): ?Money { if ($payload === null) { return null; } return new Money($payload['amount'], $payload['currency'] ?? $this->currency); } public function set($payload): ?array { if ($payload === null) { return null; } return [ 'amount' => $payload->getAmount(), 'currency' => $payload->getCurrency(), ]; } } // Use in settings class class PricingSettings extends Settings { public Money $minimum_order; public Money $shipping_fee; public static function group(): string { return 'pricing'; } public static function casts(): array { return [ 'minimum_order' => MoneyCast::class, 'shipping_fee' => new MoneyCast(null, 'EUR'), ]; } } ``` ## Artisan Commands Generate settings classes and migrations using built-in artisan commands. ```bash # Create a new settings class php artisan make:setting GeneralSettings --group=general # Create settings class in custom path php artisan make:setting BlogSettings --group=blog --path=app/Settings/Blog # Create a settings migration php artisan make:settings-migration CreateGeneralSettings # Create migration in custom path php artisan make:settings-migration CreateBlogSettings database/settings/blog # Run migrations (includes settings migrations) php artisan migrate # Cache discovered settings classes php artisan settings:discover # Clear discovered settings cache php artisan settings:clear-discovered # Clear settings cache php artisan settings:clear-cache ``` ## Configuration Options The settings.php config file provides extensive customization options for repositories, caching, auto-discovery, and global casts. ```php [ App\Settings\GeneralSettings::class, App\Settings\BlogSettings::class, ], // Path for generated settings classes 'setting_class_path' => app_path('Settings'), // Paths for settings migrations 'migrations_paths' => [ database_path('settings'), ], // Default repository for settings storage 'default_repository' => 'database', // Repository configurations 'repositories' => [ 'database' => [ 'type' => Spatie\LaravelSettings\SettingsRepositories\DatabaseSettingsRepository::class, 'model' => null, 'table' => 'settings', 'connection' => null, ], 'redis' => [ 'type' => Spatie\LaravelSettings\SettingsRepositories\RedisSettingsRepository::class, 'connection' => 'default', 'prefix' => 'settings:', ], ], // Custom encoder/decoder 'encoder' => fn($value): string => json_encode($value), 'decoder' => fn(string $payload, bool $associative) => json_decode($payload, $associative), // Cache configuration 'cache' => [ 'enabled' => env('SETTINGS_CACHE_ENABLED', false), 'store' => null, 'prefix' => 'settings.', 'ttl' => 3600, 'memo' => env('SETTINGS_CACHE_MEMO', false), // Laravel 12.9+ ], // Global casts applied automatically 'global_casts' => [ DateTimeInterface::class => Spatie\LaravelSettings\SettingsCasts\DateTimeInterfaceCast::class, DateTimeZone::class => Spatie\LaravelSettings\SettingsCasts\DateTimeZoneCast::class, Spatie\LaravelData\Data::class => Spatie\LaravelSettings\SettingsCasts\DataCast::class, ], // Auto-discover settings in these paths 'auto_discover_settings' => [ app_path('Settings'), ], // Cache path for discovered settings 'discovered_settings_cache_path' => base_path('bootstrap/cache'), ]; ``` ## Summary Laravel Settings is ideal for managing application-wide configuration that needs to be editable at runtime without code deployments. Common use cases include site settings (name, logo, maintenance mode), feature flags, notification preferences, API configurations, pricing settings, and per-tenant configurations in multi-tenant applications. The typed properties ensure data integrity while the migration system provides version control for settings schema changes. Integration with Laravel is seamless through dependency injection, events, and familiar migration patterns. The package works well with spatie/laravel-data for complex nested settings, supports multiple storage backends (database and Redis), and provides caching for production performance. For testing, the fake() method allows complete control over settings values without touching the repository, making unit and feature tests predictable and fast.