tzunghaor/settings-bundle
A PHP settings bundle for managing configurable application values with scopes, typed settings, and a simple key-value API. Store defaults and overrides in the database and read them anywhere in your app. Lightweight and easy to integrate.
composer require <vendor>/<package>:^0.18
php artisan vendor:publish --provider="<Vendor>\<Package>\<PackageServiceProvider>"
php artisan migrate
config/<package>.php — driver, cache store, table names, locale/fallback settings.Settings/Setting definition classes in app/ — these are the source of truth for keys, types, defaults, and (in 0.18) form metadata.App\Settings\Seo class-typed setting. Define the settings, render the auto-generated form, save, then read values in a Blade view and an API resource.Typed settings with explicit defaults. Define each setting with a key, a type, and a default. Treat the definition classes as the contract; everything else (forms, validation, casting) should derive from them rather than duplicating rules in controllers.
Nullable settings (new in 0.18). Use null to mean "not configured" and reserve false, 0, and '' for deliberate values. This is the main win of the release — you can now distinguish "unset" from "empty":
$email = setting('support.email'); // null when never set
$email ??= config('mail.from.address');
Apply the same discipline in validation: ['nullable', 'email'] rather than ['required'], and in the form layer use a "clear" affordance rather than forcing an empty string.
Translation via symfony/translation (new in 0.18). For user-facing strings, store per-locale values and resolve with the current locale, falling back to the configured fallback locale. Keep translation keys stable and versioned; the underlying symfony/translation component means you can also feed the same catalog into existing translator tooling. Prefer reading translated settings through a single helper/resolver rather than sprinkling app()->getLocale() checks across views.
Class-typed settings with auto-generated forms (new in 0.18). Model a settings group as a class (e.g. Seo, Billing, SocialLinks) with typed public properties or promoted constructor parameters. The package reflects over the class to build the form. Pattern:
final class Seo
{
public function __construct(
public string $title = '',
public ?string $description = null,
public bool $indexable = true,
) {}
}
Register it once, then let the generated form handle create/edit. Where reflection is insufficient (enums, nested objects, custom widgets), override only that field instead of hand-rolling the whole form.
Grouping and reading. Group related keys under a namespace (billing.*, seo.*) so cache invalidation and permission checks can be scoped per group. Read via the package helper in app code, and via a cached accessor in hot paths (API resources, middleware) to avoid repeated resolution.
Integration tips.
settings:clear step to deploy scripts.'' or 0 that now look like real values. Write a one-off migration to normalise legacy empties to null if you intend "unset" semantics.DateTimeInterface, nested setting classes, and arrays of objects. Check what the generator emits for each before promising a no-code admin UI; keep a manual form class as the escape hatch.symfony/translation version constraints. The new dependency can conflict with an older Laravel/Symfony lock. Run composer why symfony/translation before upgrading and test the fallback-locale path, not just the happy path.null. This matters for nullable fields — a null translation and a missing translation can look identical.How can I help you explore Laravel packages today?