spatie/laravel-settings

repository·main·Indexed 23 days ago

https://github.com/spatie/laravel-settings

A Laravel package for storing strongly typed application settings using classes, migrations, and repositories such as database or Redis. It features support for complex type casting, property encryption, locking, and settings caching. The package allows for the definition of settings via classes extending Spatie\LaravelSettings\Settings and provides a dedicated migration system to keep application code and data in sync.

Tokens
6.2K
Snippets
14
Records
21
Agent score
79%

What's inside spatie/laravel-settings

  1. Configure settings repositories

    main

    Settings are stored in repositories. The package supports database and redis types. You can define multiple repositories in config/settings.php.

    Database Repository options:

    • model: The Eloquent model used for storage.
    • table: The database table name.
    • connection: The database connection.

    Redis Repository options:

    • prefix: An optional prefix for keys.
    • connection: The Redis connection.

    Assigning a repository to a settings class: Implement the repository() static method in your settings class. If not implemented, the default_repository from config/settings.php is used.

    class GeneralSettings extends Settings
    {
        // ...
        public static function repository(): ?string
        {
            return 'global_settings';
        }
    }
    public static function repository(): ?string
    {
        return 'global_settings';
    }
  2. Auto-discover settings classes

    main

    Instead of manually adding every settings class to config/settings.php, you can enable auto-discovery. The package will scan your application for settings classes.

    1. Configure the paths to scan in the auto_discover_settings array in config/settings.php.
    2. Cache the discovered classes for better performance using:
      php artisan settings:discover
    3. To clear the discovery cache:
      php artisan settings:clear-discovered
    php artisan settings:discover
  3. Upgrade from v2 to v3

    main

    Upgrading from version 2 to version 3 involves changes to settings discovery, custom repository implementations, and the database schema.

    Settings Discovery

    In v3, the default search location for settings classes is app_path('Settings'). If you wish to maintain the v2 behavior of searching the base app_path(), you must set the auto_discover_settings option in your configuration to app_path().

    Custom Repositories

    If you have implemented custom repositories, you must update them to match the new interface. Specifically, the method updatePropertyPayload has been renamed to updatePropertiesPayload and is now designed to update multiple properties simultaneously.

    Database Schema Migration

    You must run a migration to update the settings table schema to support new constraints and the locked property.

    <?php
    
    use Illuminate\Database\Migrations\Migration;
    use Illuminate\Database\Schema\Blueprint;
    use Illuminate\Support\Facades\Schema;
    
    return new class extends Migration
    {
        /**
         * Run the migrations.
         */
        public function up(): void
        {
            Schema::table('settings', function (Blueprint $table): void {
                $table->boolean('locked')->default(false)->change();
    
                $table->unique(['group', 'name']);
    
                $table->dropIndex(['group']);
            });
        }
    
        /**
         * Reverse the migrations.
         */
        public function down(): void
        {
            Schema::table('settings', function (Blueprint $table): void {
                $table->boolean('locked')->default(null)->change();
    
                $table->dropUnique(['group', 'name']);
    
                $table->index('group');
            });
        }
    };
  4. Enable and manage settings caching

    main

    To improve performance, you can enable caching for settings in config/settings.php:

    'cache' => [
        'enabled' => env('SETTINGS_CACHE_ENABLED', false),
        'store' => null, // Use default or specify a cache store
        'prefix' => null,
    ],

    Commands:

    • Clear the settings cache: php artisan settings:clear-cache

    Customizing the Cache Key: By default, the cache key is prefix.Fully\Qualified\ClassName. You can override this by implementing cacheKey() in your settings class:

    public function cacheKey(): string
    {
        return 'my_custom_cache_key';
    }
    'cache' => [
        'enabled' => env('SETTINGS_CACHE_ENABLED', false),
        'store' => null,
        'prefix' => null,
    ],
  5. Lock and Encrypt settings properties

    main

    Locking Properties

    To prevent a setting from being updated, use the lock() method on the settings instance. This ensures that even if a user tries to save a new value, the original value from the repository is preserved.

    $settings->lock('birth_date', 'name'); // Lock multiple
    $settings->isLocked('birth_date');   // Check status
    $settings->unlock('birth_date');      // Unlock

    Encrypting Properties

    For sensitive data like API keys, you can encrypt properties. This can be done via the encrypted() static method or the #[ShouldBeEncrypted] attribute.

    Using the encrypted() method:

    class GeneralSettings extends Settings
    {
        public static function encrypted(): array
        {
            return ['api_key'];
        }
    }

    Using the Attribute:

    use Spatie\LaravelSettings\Attributes\ShouldBeEncrypted;
    
    class GeneralSettings extends Settings
    {
        #[ShouldBeEncrypted]
        public string $api_key;
    }

    Migrations for Encrypted Properties: When using migrations, you must use the specific encrypted methods:

    • Use $this->migrator->addEncrypted(...) instead of add().
    • Use $this->migrator->updateEncrypted(...) instead of update().
    • Use $this->migrator->encrypt(...) to convert an existing property to encrypted.
    • Use $this->migrator->decrypt(...) to convert an encrypted property to plain text.
  6. Create and run settings migrations

    main

    Because settings classes define a structure that must match the data in your repository, you must use migrations to add, rename, update, or delete properties. This ensures your application's code and data stay in sync.

    1. Generate a migration:

    php artisan make:settings-migration CreateGeneralSettings

    This creates a file in database/settings extending SettingsMigration.

    2. Define operations in the up() method:

    • Add a property: $this->migrator->add('group.property', 'default_value');
    • Rename a property: $this->migrator->rename('group.old_name', 'group.new_name'); (can also move between groups)
    • Update a property: $this->migrator->update('group.property', fn($oldValue) => 'new_value');
    • Delete a property: $this->migrator->delete('group.property');
    • Check existence: if ($this->migrator->exists('group.property')) { ... }
    • Group operations: Use $this->migrator->inGroup('group_name', function (SettingsBlueprint $blueprint) { ... }); to avoid repeating the group prefix.
    • Specify repository: Use $this->migrator->repository('redis'); to target a specific repository.

    3. Run the migration:

    php artisan migrate
    use Spatie\LaravelSettings\Migrations\SettingsMigration;
    
    return new class extends SettingsMigration
    {
        public function up(): void
        {
            $this->migrator->add('general.site_name', 'Spatie');
            $this->migrator->add('general.site_active', true);
        }
    }
  7. Use and update settings

    main

    You can access settings by injecting the settings class into your controllers or using the app() helper. To persist changes, update the properties on the instance and call the save() method.

    Accessing settings via injection:

    class IndexController
    {
        public function __invoke(GeneralSettings $settings){
            return view('index', [
                'site_name' => $settings->site_name,
            ]);
        }
    }

    Accessing settings via app helper:

    function getName(): string{
        return app(GeneralSettings::class)->site_name;
    }

    Updating settings:

    class SettingsController
    {
        public function __invoke(GeneralSettings $settings, GeneralSettingsRequest $request){
            $settings->site_name = $request->input('site_name');
            $settings->site_active = $request->boolean('site_active');
            
            $settings->save();
            
            return redirect()->back();
        }
    }
    $settings->site_name = $request->input('site_name');
    $settings->save();
  8. Create a settings class

    main

    Settings are defined using classes that extend Spatie\LaravelSettings\Settings. Each class must have a static group() method that returns a string representing the settings group. Properties within the class represent individual settings.

    To generate a new settings class via Artisan, use:

    php artisan make:setting SettingName --group=groupName

    After creating the class, you must register it in the settings array within your config/settings.php file to allow Laravel to load it:

    'settings' => [
        GeneralSettings::class,
    ],
    use Spatie\LaravelSettings\Settings;
    
    class GeneralSettings extends Settings
    {
        public string $site_name;
        
        public bool $site_active;
        
        public static function group(): string
        {
            return 'general';
        }
    }
  9. Install spatie/laravel-settings

    main

    Install the package via Composer and set up the necessary database migrations.

    composer require spatie/laravel-settings
    
    php artisan vendor:publish --provider="Spatie\LaravelSettings\LaravelSettingsServiceProvider" --tag="migrations"
    php artisan migrate
  10. Use casts for complex types

    main

    While simple types (string, int, bool, array) are automatically converted to JSON, complex types like DateTime, Carbon, or custom objects require Casts.

    Local Casts

    Defined within the settings class using the casts() method. This is useful for specific properties.

    class DateSettings extends Settings
    {
        public DateTime $birth_date;
        
        public static function group(): string
        {
            return 'date';
        }
    
        public static function casts(): array
        {
            return [
                'birth_date' => DateTimeInterfaceCast::class
            ];
        }
    }

    Advanced Local Casts:

    • With arguments (string syntax): 'property' => CastClass::class . ':' . Argument::class
    • With arguments (object syntax): 'property' => new CastClass(Argument::class, 'timezone')

    Global Casts

    Defined in config/settings.php under the global_casts key. If a property type matches a key in global_casts, the caster is applied automatically.

    Built-in Casts:

    • DateTimeInterfaceCast: For DateTime, DateTimeImmutable, Carbon, CarbonImmutable.
    • DateTimeZoneCast: For DateTimeZone objects.
    • DataCast: For Spatie\LaravelData\Data objects.
    • DataArrayCast: For arrays of Spatie\LaravelData\Data objects.
    • EnumCast: For native PHP enums.
    • CollectionCast: For Illuminate\Support\Collection objects.
    public static function casts(): array
    {
        return [
            'birth_date' => DateTimeInterfaceCast::class
        ];
    }
  11. Configure the laravel-settings package

    main

    You can publish the configuration file to customize how settings are stored, cached, and discovered.

    php artisan vendor:publish --provider="Spatie\LaravelSettings\LaravelSettingsServiceProvider" --tag="config"

    Key configuration options include:

    • settings: Manual registration of settings classes.
    • setting_class_path: The directory where settings classes are located.
    • migrations_paths: Directories where settings migrations are stored.
    • default_repository: The default repository used if none is specified for a class (e.g., database).
    • repositories: Definitions for available repositories (e.g., database, redis).
    • cache: Configuration for caching settings (enabled via SETTINGS_CACHE_ENABLED env var).
    • global_casts: Automatic casts for non-default PHP types.
    • auto_discover_settings: Directories to scan for settings classes.
    • discovered_settings_cache_path: Path for caching discovered settings.
    return [
    
        /*
         * Each settings class used in your application must be registered, you can
         * put them (manools) here.
         */
        'settings' => [
    
        ],
    
        /*
         * The path where the settings classes will be created.
         */
        'setting_class_path' => app_path('Settings'),
    
        /*
         * In these directories settings migrations will be stored and ran when migrating. A settings
         * migration created via the make:settings-migration command will be stored in the first path or
         * a custom defined path when running the command.
         */
        'migrations_paths' => [
            database_path('settings'),
        ],
    
        /*
         * When no repository was set for a settings class the following repository
         * will be used for loading and saving settings.
         */
        'default_repository' => 'database',
    
        /*
         * Settings will be stored and loaded from these repositories.
         */
        'repositories' => [
            'database' => [
                'type' => Spatie\LaravelSettings\SettingsRepositories\DatabaseSettingsRepository::class,
                'model' => null,
                'table' => null,
                'connection' => null,
            ],
            'redis' => [
                'type' => Spatie\LaravelSettings\SettingsRepositories\RedisSettingsRepository::class,
                'connection' => null,
                'prefix' => null,
            ],
        ],
    
        /*
         * The encoder and decoder will determine how settings are stored and
         * retrieved in the database. By default, `json_encode` and `json_decode`
         * are used.
         */
        'encoder' => null,
        'decoder' => null,
    
        /*
         * The contents of settings classes can be cached through your application,
         * settings will be stored within a provided Laravel store and can have an
         * additional prefix.
         */
        'cache' => [
            'enabled' => env('SETTINGS_CACHE_ENABLED', false),
            'store' => null,
            'prefix' => null,
            'ttl' => null,
        ],
    
        /*
         * These global casts will be automatically used whenever a property within
         * your settings class isn't a default PHP type.
         */
        'global_casts' => [
            DateTimeInterface::class => Spatie\LaravelSettings\SettingsCasts\DateTimeInterfaceCast::class,
            DateTimeZone::class => Spatie\LaravelSettings\SettingsCasts\DateTimeZoneCast::class,
         // Spatie\DataTransferObject\DataTransferObject::class => Spatie\LaravelSettings\SettingsCasts\DtoCast::class,
            Spatie\LaravelData\Data::class => Spatie\LaravelSettings\SettingsCasts\DataCast::class,
        ],
    
        /*
         * The package will look for settings in these paths and automatically
         * register them.
         */
        'auto_discover_settings' => [
            app_path('Settings'),
        ],
    
        /*
         * Automatically discovered settings classes can be cached, so they don't need to be
         * searched each time the application boots up.
         */
        'discovered_settings_cache_path' => base_path('bootstrap/cache'),
    ];
  12. Update settings values

    main

    To update settings, modify the properties on the injected settings instance and call the save() method.

    class GeneralSettingsController
    {
        public function update(
            GeneralSettingsRequest $request,
            GeneralSettings $settings
        )
        {
            $settings->site_name = $request->input('site_name');
            $settings->site_active = $request->input('site_active');
            
            $settings->save();
            
            return redirect()->back();
        }
    }
    class GeneralSettingsController
    {
        public function update(
            GeneralSettingsRequest $request,
            GeneralSettings $settings
        ){
            $settings->site_name = $request->input('site_name');
            $settings->site_active = $request->input('site_active');
            
            $settings->save();
            
            return redirect()->back();
        }
    }