marshmallow/nova-tool-settings
A Laravel Nova tool for managing settings for your application and Nova tools. Define settings in code, let users edit them through a clean Nova interface, and retrieve stored values anywhere in your Laravel app, keeping configuration editable without redeploying. Simple to install and integrate.
Install the package
composer require marshmallow/nova-tool-settings
Note: the package is archived and pinned to older Nova versions. Before installing, check composer.json in the repo for the laravel/nova constraint and confirm it matches your project. If you're on Nova 4+ you will likely need to fork and bump the constraint.
Publish and run the migration
php artisan vendor:publish --provider="Marshmallow\NovaToolSettings\ToolServiceProvider"
php artisan migrate
This creates the storage table the tool reads from. Inspect the published migration first — you want to know the table name (typically settings) so you can write seeders or factories against it.
Register the tool with Nova
In app/Providers/NovaServiceProvider.php:
public function tools()
{
return [
new \Marshmallow\NovaToolSettings\NovaToolSettings,
];
}
The tool appears in Nova's sidebar. Open it once before you wire up any settings — an empty tool is the fastest way to confirm routing and the container binding are working.
Define your first setting "from code"
The package's core idea is that settings are declared in code (so the UI has a schema to render) and persisted in the DB. Look at src/ for the registration entry point — it's usually a service provider or a config-driven list. A minimal definition looks conceptually like:
Settings::group('General', function ($group) {
$group->text('site_name', 'Site name', default: config('app.name'));
$group->textarea('footer_text', 'Footer');
$group->boolean('maintenance_mode', 'Maintenance mode');
});
First use case: expose site_name and maintenance_mode, edit them in Nova, then read them in a Blade view or middleware via the package's helper/facade (check src/ for the exact accessor — it varies between Settings::get('site_name') and a Setting::where(...)->first()->value pattern).
Read-through with a helper wrapper. Don't sprinkle raw facade calls across the codebase. Wrap the package in a small app-level SettingsService so you have one place to swap the backend later (this package is archived — you want that seam):
class SettingsService
{
public function siteName(): string
{
return Settings::get('site_name', config('app.name'));
}
public function maintenanceMode(): bool
{
return (bool) Settings::get('maintenance_mode', false);
}
}
Group settings by domain. Group definitions by feature (general, mail, integrations, billing) rather than one giant list. The Nova tool renders groups as tabs/sections, and grouping keeps the admin screen navigable as you add settings.
Cache aggressively, invalidate on write. Settings are read on nearly every request. Cache the whole map in a single key and bust it from the write path:
Cache::rememberForever('app.settings', function () {
return Settings::all()->pluck('value', 'key')->toArray();
});
Then in an observer or a Nova saved hook, call Cache::forget('app.settings'). If you're writing settings from the tool UI, the package may not clear your cache for you — verify and add it yourself.
Seed defaults for CI and fresh installs. Settings "created from code" should also exist as database rows, otherwise a fresh deploy renders empty. Write a seeder that iterates your definitions and inserts defaults with updateOrCreate, so re-running is safe.
Gate the tool behind a policy. Admins-only is the common case. Check whether the tool supports Nova's canSee / authorize; if not, gate the tool's route with a middleware or a Nova gate in Nova::tools([...]) via a conditional.
Environment overrides. For settings that differ per environment (e.g. mail_from, support_email), prefer config + env and expose only the settings that genuinely need runtime editing. Global settings tools invite people to put secrets in the database — avoid that for credentials.
Pair with feature-flag style reads. A boolean settings row is a cheap feature flag: if (app(SettingsService::class)->maintenanceMode()) { abort(503); } in middleware. It's a good first integration because it exercises the full read→cache→UI loop end to end.
booted vs boot, component registration) changed between v3 and v4. If the tool installs but the sidebar entry 404s or renders blank, suspect the Nova constraint first — check composer.lock after install.settings table from spatie/laravel-settings or similar. Rename the table and the model's $table if needed.(bool), (int), json_decode) at your service layer — don't rely on the package's casting, especially if it was written for an older Laravel that handled model casts differently.tinker query on the underlying table (Setting::all()). If the row is missing, the code definition never ran — check that your service provider is auto-discovered or registered in config/app.php, and that it runs after the database is available (don't query during register()).cards()/fields(), and (b) a custom Nova resource if you outgrow the built-in screen. Prefer option (b) once you need validation rules, field-level permissions, or audit logging — retrofitting those onto a single-screen tool is more work than writing a Nova resource yourself.How can I help you explore Laravel packages today?