sashalenz / laravel-settings-ui
Declarative, nestable runtime settings for Laravel โ fluent schema, pluggable stores, secure secrets, resilient cache, config() overlay and bundled admin UI.
Requires
- php: ^8.3|^8.4|^8.5
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/validation: ^11.0|^12.0|^13.0
- livewire/livewire: ^3.5|^4.0
- spatie/laravel-package-tools: ^1.16|^1.17|^1.93
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- nunomaduro/collision: ^8.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpstan/phpstan: ^2.0
Suggests
- aws/aws-sdk-php: Required to use AwsSecretsManagerDriver.
- google/cloud-secret-manager: Required to use GcpSecretManagerDriver.
- spatie/laravel-activitylog: Bind ActivitylogRecorder to mirror setting changes into an existing activity log.
Provides
None
Conflicts
None
Replaces
None
README
Declarative, nestable runtime settings for Laravel โ fluent schema, pluggable stores, secure secrets, resilient cache, transparent config() overlay, and a bundled responsive admin UI built with Livewire and Tailwind CSS.
โจ Features
- Code-Authoritative Declarations: Settings are defined in strongly typed PHP classes, never in the database.
- Zero-Coupling
config()Overlay: Application code continues to readconfig('group.key')without any dependency on the settings machinery. - Deep Hierarchical Nesting: Groups, subgroups, cards, sections, and fields mirror full dotted paths (
group.section.key). - Pluggable Storage Backends: Database, Encrypted Database, File (JSON/YAML), and external Secret Stores (Vault, AWS Secrets Manager, etc.).
- Pluggable & Secure Secrets Engine:
- Masked values in HTML and Livewire states.
- Zero-plaintext leaks in logs and exception traces.
- Rate-limited and permission-gated "Click to Reveal" action.
- Resilient & Stampede-Protected Cache:
- Stores primitive values only (zero risk of
__PHP_Incomplete_Classserialization bugs). - Schema fingerprinting & version-stamped keys.
- Cache stampede prevention via cache locks.
- Queue worker / Octane auto-refresh hooks.
- Stores primitive values only (zero risk of
- Full Audit History & Revert:
- Automatically records user, timestamp, before/after values, and source.
- One-click rollback to prior versions.
- Modular & Domain Discovery:
- Register settings from packages, modules, or domains (
Settings::register(...)orSettings::discover(...)). - Composer
extra.laravel-settingsdiscovery.
- Register settings from packages, modules, or domains (
- Seeder Engine & CLI:
SettingsSeederfor idempotent seeding (syncMissing()andforceSync()).- Artisan commands:
settings:sync,settings:seed,settings:doctor,settings:clear-cache.
๐ฆ Installation
Install via Composer:
composer require sashalenz/laravel-settings-ui
Publish configuration and migrations:
php artisan vendor:publish --tag="settings-config" php artisan vendor:publish --tag="settings-migrations" php artisan migrate
๐ Quick Start
1. Declare a Setting Schema
Create a schema class implementing DeclaresSettings:
namespace App\Settings; use SashaLenz\SettingsUi\Contracts\DeclaresSettings; use SashaLenz\SettingsUi\Schema\Field; use SashaLenz\SettingsUi\Schema\Group; final class GeneralSettings implements DeclaresSettings { public function schema(): Group { return Group::make('general') ->label('General Settings') ->icon('heroicon-o-cog') ->fields([ Field::text('site_name') ->label('Website Name') ->default('My App') ->rules(['required', 'string', 'max:255']), Field::boolean('maintenance_mode') ->label('Maintenance Mode') ->default(false), Field::text('api_token') ->label('API Secret Token') ->secret() ->store('secrets'), ]); } }
2. Register the Schema
In your config/settings.php:
'schemas' => [ \App\Settings\GeneralSettings::class, ],
Or programmatically in any ServiceProvider:
use SashaLenz\SettingsUi\Facades\Settings; public function boot(): void { Settings::register([ \App\Settings\GeneralSettings::class, ]); // Or discover all settings across domain modules: Settings::discover([ app_path('Domain/*/Settings'), ]); }
3. Read Values Transparently
// Stored database value automatically overlays config file default: $siteName = config('general.site_name'); // Or using the Settings facade: $siteName = Settings::get('general.site_name');
๐ Secrets Management
Mark fields as secret to encrypt them and prevent plaintext exposure in forms and logs:
Field::text('stripe_secret') ->secret() ->store('secrets')
Supported Drivers:
encrypted-db: Encrypted locally in your database using Laravel'sAPP_KEY.env: Resolves from.envenvironment variables.vault: HashiCorp Vault KV v1/v2 engine.aws: AWS Secrets Manager.
๐ฑ Seeding Settings
Seed declared defaults safely without overwriting existing settings:
use SashaLenz\SettingsUi\Database\Seeders\SettingsSeeder; class DatabaseSeeder extends Seeder { public function run(): void { $this->call(SettingsSeeder::class); } }
Or via Artisan CLI:
php artisan settings:seed php artisan settings:seed --group=general --force
๐ฅ Admin UI
Mount the routes in your routes file (e.g., routes/web.php):
use SashaLenz\SettingsUi\Facades\Settings; Route::prefix('admin')->middleware(['web', 'auth'])->group(function () { Settings::routes(); });
Available routes:
/admin/settingsโ List of setting groups/admin/settings/{group}โ Group editor form/admin/settings/historyโ Audit change log & revert/admin/settings/schemaโ Read-only schema inspector
๐งช Testing
composer test
composer format
composer analyse
๐ License
The MIT License (MIT). Please see License File for more information.
Lists and repeaters
Use a list for strings, or a repeater for objects with declared child fields:
Field::list('tags')->default(['news'])->minItems(1)->maxItems(10)->itemLabel('Tag'), Field::repeater('links') ->schema([ Field::text('label')->rules('required|string|max:100'), Field::text('url')->rules('required|url'), ]) ->default([['label' => 'Home', 'url' => 'https://example.com']]) ->minItems(1)->maxItems(5)->itemLabel('Link'),
Both default to an empty array. The UI offers add, remove, move up, and move down
controls. itemLabel() accepts a literal or translation key followed by the row
number. Child defaults initialize new repeater rows. Schemas contain unique,
non-secret scalar fields; nested collections are not supported.
Collection rules include array and the configured item limits. Lists validate
each data.path.* as a string; repeater child rules use data.path.*.child.
Use rules() on a collection for additional array-level constraints. Empty
lists are allowed unless a minimum or required rule is declared.
Values persist as JSON arrays through the existing database/file stores and
remain PHP arrays in the config overlay. Removing or moving rows reindexes the
array. Collection defaults must be arrays. secret(), encrypted(), and
external secret bindings are not allowed on lists/repeaters.
The Livewire form actions addRow($path), removeRow($path, $index), and
moveRow($path, $from, $to) accept paths relative to the group (without data.).