quadrubo/filament-model-settings
Filament plugin for managing per-model settings in your admin panel. It adds a settings page and reusable form component so you can store, edit, and read model-specific configuration values directly from Filament with a simple API.
This assessment covers quadrubo/filament-model-settings at v2.0.1. That patch release is a follow-up to the v2.0.0 major; nothing in the v2.0.1 changelog signals breaking changes, so anything you build against v2.0.0 semantics carries forward. Treat v2.0.0 as the reference point for upgrade notes (Filament major alignment, PHP/Laravel floor) and v2.0.1 as a drop-in patch.
Day-to-day, the package solves one specific problem: giving individual Eloquent records their own editable settings, with a Filament UI to edit them — as opposed to app-wide config or a single settings singleton. If your need is "the tenant/team/user/project record has its own preferences that an admin edits in the panel," this is the right shape of tool. If your need is global app settings, use spatie/laravel-settings instead; don't bend this package to that job.
Minimal path to first value:
composer require quadrubo/filament-model-settings and publish + run the migration (there is a settings storage table; check the published file for column names before you write queries against it).$model->settings (or the package's accessor) returns your values.Where to look first, in order: the published config (panel/UI options), the published migration (storage shape), then the README's model-registration example. Confirm the exact trait name, settings-class base/interface, and accessor signature against the README for the installed version before you write production code against them — v2 reorganised namespaces and these are the identifiers most likely to differ from older blog posts and Stack Overflow answers.
First use case to ship: a single NotificationSettings class on User with three booleans, wired into the panel, read in a queued job. That exercises save, load, defaults, and serialisation in one afternoon and surfaces every gotcha below before you commit to a wider rollout.
Settings class as the single source of truth. Define typed properties with sane defaults and put validation rules next to them. Every read site ($user->settings->notify_email) then depends on one class, not on string keys scattered across the codebase. Resist adding a parallel config() fallback — you get two sources of truth and drift within a sprint.
Read-through accessor, write-through form. The Filament form is just a view over the settings class. Keep business rules in the class (validation, casting, defaults) so seeders, console commands, API endpoints, and the panel all behave identically. If a rule only exists in the Filament form schema, it doesn't exist for the importer.
Per-model resolution helper. Wrap lookups in a small service or repository method rather than calling the accessor inline everywhere:
final class SettingsResolver
{
public function for(Model $model): ModelSettings
{
return $model->settings ?? ModelSettings::defaults();
}
}
You will want one place to change when you add caching, tenancy scoping, or default-merging later.
Defaults for unsaved models. New records have no settings row. Decide explicitly whether reads return null, a defaults object, or lazily create a row, and encode that decision once. Lazy-creating on read inside a GET request is a trap — it writes during page render and breaks read-only replicas and tests that assert query counts.
Multi-tenant panels. Register the plugin per panel and scope the settings lookup by the tenant. Verify the settings table has (or you add) a tenant discriminator; sharing one settings row across tenants is a data-leak class of bug, not a cosmetic one.
Policy-gated editing. Gate the settings page/tab behind a policy ability (updateSettings) rather than relying on panel access alone. Settings pages are commonly attached to resources where "can view" and "can reconfigure" are different permissions — support staff who can read a customer record should not necessarily toggle that customer's notification routing.
Queues and long-running workers. Load settings at job dispatch time or re-read at the start of handle(), not in the constructor of a singleton service. A worker that boots once and caches a model's settings will serve stale values for the life of the process.
Migrations from a previous version. Because v2 is a major, expect namespace and registration changes rather than storage changes. Diff the published migration from your installed copy before deploying; if the table shape moved, write a data migration rather than trusting migrate to be non-destructive.
CHANGELOG/commits touching namespaces, migrations, and the plugin class) and run your test suite against the new tag. Patch version ≠ guaranteed no-op when the notes are empty.->refresh() after save in tests. Assertions on $model->settings immediately after a Filament form submit often read a stale instance. Re-fetch from the database to test what actually persisted.->with('settings')) or use withCount. Watch DB::listen on the first table you build.text payload limits. If settings are stored as a serialised blob, a growing settings class eventually hits column limits or produces unreadable rows. Prefer flat, typed properties; avoid nesting arbitrary arrays in settings "for flexibility.""false" is truthy, "0" is falsy, and null vs. absent-key are different. Cast on read in the settings class and unit-test the round-trip for booleans, enums, and dates — this is where most real bugs in settings packages live.livewire()/callAction() test helpers for the settings page and RefreshDatabase with the package migration. A settings package you can't test in isolation becomes a package you stop trusting.updated_at and a clear "settings changed, please review" UX.How can I help you explore Laravel packages today?