Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Nova Settings Tool Laravel Package

bakerkretzmar/nova-settings-tool

A Laravel Nova tool for managing application settings directly from your Nova admin panel. Integrates with spatie/laravel-settings, letting you edit and organize multiple settings classes through a clean, configurable interface without writing custom screens. Quick to install and configure.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites (verify before you start). Laravel Nova 4 or 5 — as of 2.1.0 Nova 5 is supported (PR #62), but the release notes don't state whether the constraint widened to ^4.0|^5.0 or moved to ^5.0 only. Read composer.json on the 2.1.0 tag first; that one check decides whether this is a routine bump or a sequencing prerequisite. Nova 5 in turn implies PHP 8.2+ and a modern Laravel, so reconcile both axes before installing. You also need spatie/laravel-settings as the persistence backend — if the app doesn't already use it, adopt it as a separate, independently justified step rather than bundling it into "install the settings UI."

Install.

composer require bakerkretzmar/nova-settings-tool
# settings backend, if not already present
composer require spatie/laravel-settings
php artisan vendor:publish --provider="Spatie\LaravelSettings\LaravelSettingsServiceProvider" --tag="migrations"
php artisan migrate

Publish the tool's config if one ships with the tag, then register the tool:

// app/Providers/NovaServiceProvider.php
public function tools()
{
    return [
        new \Bakerkretzmar\NovaSettingsTool\NovaSettingsTool,
    ];
}

Where to look first, in order:

  1. composer.json on the tag — Nova/Laravel/PHP constraints (the gate above).
  2. The package README + src/ — specifically the base settings-page class you extend and how it maps a Nova field to a settings property. The mapping is by field name → settings property name, and the exact method signatures are worth confirming against the tag rather than trusting a blog post.
  3. The package's own tests — they tell you whether Nova 5 is actually exercised in CI or only smoke-tested, and they double as copy-paste fixtures.

First use case — one page, one field. Define a settings class with a sane default:

// app/Settings/GeneralSettings.php
class GeneralSettings extends Settings
{
    public string $site_name = 'My App';
    public bool $maintenance_banner = false;
}

Expose it as a Nova page with fields whose names match the properties exactly:

class General extends Settings  // base class from the package
{
    public $settings = GeneralSettings::class;

    public function fields(): array
    {
        return [
            Text::make('Site Name', 'site_name')->rules('required', 'max:255'),
            Boolean::make('Maintenance Banner', 'maintenance_banner'),
        ];
    }
}

Read it anywhere via the class, not a global:

$settings = app(GeneralSettings::class);
return view('welcome', ['name' => $settings->site_name]);

Ship this one page, confirm a write survives a cache clear and a deploy, then expand. The first page is a wiring test, not a deliverable.

Implementation Patterns

Settings classes are the source of truth; Nova pages are a projection. Every property exists in a version-controlled PHP class with a typed declaration and a default. The Nova page only names the fields. Keep all business logic out of the page class so that removing the package is a one-commit change — if a lagging release blocks a Nova upgrade, you delete the page classes and the tool registration and the app keeps working.

One page per ownership group. This is the pattern that scales: a General page for branding/copy, a Notifications page for thresholds, a Features page for flags, an Integrations page for endpoints. Each page gets an owner in the code comment and a policy. Resist the single giant page — it makes authorization coarse and diffs unreviewable.

Per-group authorization. Nova's canSee() / the tool's authorization hook is the only thing between a Nova login and every setting. Default behaviour is effectively "any Nova user," which is wrong for a settings screen. Add a policy or canSee() per page, and gate the tool itself:

public function tools()
{
    return [
        (new NovaSettingsTool)->canSee(fn ($request) => $request->user()->isAdmin()),
    ];
}

Read via container/DI, not in loops. The settings repository is cached, but resolution isn't free. Inject the settings class into a service or view composer once per request; don't call app(GeneralSettings::class) inside a Blade loop over 500 rows.

Feature flags. Declare public bool $new_checkout = false; and a Boolean Nova field. Cast in the settings class (typed property or a cast) rather than at the call site — Nova text fields hand back strings, and if ($settings->threshold) on a string "0" is false-y in confusing ways only sometimes.

Defaults for new environments. Settings migrations (spatie's make:settings-migration command) are how a fresh environment gets values. Treat them like schema: a new setting property without a migration means production has no value until someone opens the UI. Add the migration in the same PR as the property.

Test the round trip, not the UI. A write-then-read test catches the silent staleness class of bug:

$settings = app(GeneralSettings::class);
$settings->site_name = 'Changed';
$settings->save();

$this->assertSame('Changed', app(GeneralSettings::class)->site_name);

Add a second assertion after $this->artisan('cache:clear') if the settings cache is a separate store. This test is cheap and it is the one thing that fails loudly when cache invalidation regresses.

Keep a definition test. Assert that every Nova field name on a page has a matching property on its settings class. Field/property drift is the most common defect in this pattern and it fails at runtime, in production, with a null.

Integration tips. Use Nova's native ->rules(), ->help(), and ->placeholder() to enforce and document shape at the point of entry — there is no other validation layer. Use ->dependsOn() sparingly; conditional fields make the "what can an admin change" surface harder to reason about. Anything that needs approval or a staged rollout does not belong here.

Gotchas and Tips

The Nova constraint is the whole decision. If 2.1.0 is a hard ^5.0 floor, anyone mid-Nova-upgrade is pinned to 2.0.5 and this becomes a sequenced prerequisite rather than a patch bump. Never upgrade Nova and this tool in the same change: if the tool fails to boot, you won't know which side broke it.

A tool-registration exception takes down the entire Nova panel, not just the settings screen. After any upgrade, boot Nova in staging and load /nova before you trust it. If you want insurance, wrap the tool registration in a config flag so you can disable the settings screen without a deploy.

Field names must match property names exactly. A mismatch is silent at definition time — the field renders, saves nothing, and the app reads the default. This is the number one debugging session with this package; check it before you check anything else.

Cache staleness is the expensive failure mode. Stale settings don't throw — they produce wrong behaviour that looks like a business-logic bug. After any write, confirm the new value is visible in a fresh request, and include a cache-clear step in your deploy checklist when adding settings.

Don't put secrets in here. Values land in plaintext in the settings store. If you must hold an API key, use an encrypted cast and treat the file/table permissions as a security requirement with a named owner — and honestly, a file-backed admin screen is a weaker answer than an encrypted DB column. Credentials belong in env/secret manager.

Concurrency is last-write-wins. A file/JSON-backed store is not transactional. Two admins editing different pages at the same time is fine; the same page is not, and there's no audit trail telling you who changed a threshold at 3am. If you need history or approval, this is the wrong tool.

Type coercion bites at the boundary. Nova text fields return strings; declare typed properties or explicit casts on the settings class so the rest of the app sees int/bool, not "10"/"1".

Single maintainer, zero recorded dependents. Release cadence is gated by Nova's major cycle, community-only support. Designate an internal owner who watches Nova releases and can pin or fork. Keep the removal path short — settings classes usable without the UI, page definitions isolated — so a blocked release costs you a commit, not a migration.

Debugging checklist. Settings value is wrong → read it directly in php artisan tinker via the settings class (isolates storage from UI). UI doesn't persist → check field/property name match, then the settings save path. Tool doesn't appear → storage/logs/laravel.log for the boot exception, then confirm the tool is in NovaServiceProvider::tools(). Value changes on one node but not another → settings cache store, not the UI.

Extension points worth knowing. Custom Nova fields drop straight into fields() — that's the intended extension surface, and it's where most of your real work lives. Per-group canSee() and a resolveUsing/fillUsing on individual fields cover most edge cases without forking. If you find yourself needing to override the package's page internals, that's a signal you wanted a bespoke settings screen.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console