resham/nova-dependent-filter
A Laravel Nova package that adds dependent filters, letting one filter's options update based on another's selected value. Build cascading filter logic in Nova resource index views with minimal setup and no custom JavaScript required.
Architecture fit. The package is a point solution that lives entirely inside Laravel Nova's filter panel. It does not stand alone: it augments Nova's existing Filter contract so that one filter's option set resolves against the value of a parent filter. That makes it architecturally clean — no new services, no schema changes, no background jobs — but also means it inherits Nova's undocumented frontend internals as its foundation. Fit is high only if the product already runs Nova and the need is genuinely cascading filters on admin screens. It is a back-office operator-efficiency tool, not a customer-facing capability, and should be scoped as such from the outset.
Integration feasibility. Mechanically straightforward for a team already on Nova: composer require, extend the package's base filter class rather than Laravel\Nova\Filters\Filter, declare parent(), register the parent filter before the child in the resource's filters() method, and run nova:publish. The trap is a hard prerequisite: installation fails dependency resolution if laravel/nova is not already installed and locked, and the error message will not say so. Feasibility therefore hinges on three pre-checks that should be done before any commitment: the exact Nova major pinned by the package (4.x vs 5.x, Vue 2 vs Vue 3), the PHP floor, and whether it ships precompiled JS or raw .vue requiring a consumer build. If the team's Nova version falls outside that constraint, the frontend component will not mount and the whole evaluation is over.
Technical risk. Concentrated in ownership rather than code complexity:
composer.json says.options() is not scoped through the same policies as the index query, a user who can see the child filter can enumerate records they cannot view. This is the one risk with a security dimension, not just an availability dimension.options() that plucks the full set when the parent is blank turns the first page load of a large admin index into an unbounded query. This is a predictable, self-inflicted incident pattern.Key questions.
options() have any authorization awareness, or is policy scoping entirely our responsibility in the subclass?Stack fit. Requires Laravel + Nova, full stop. It brings no new runtime, no new datastore, no queue or cron surface. The only frontend implication is asset publishing: Nova assets are published per deploy, not per request, so a stale-JS mismatch between server and source is a live failure mode. For a team already operating a Nova deploy pipeline, this is an incremental step, not a new capability.
Migration path. There is nothing to migrate — it is additive. Introduce it behind an internal wrapper so the third-party code is isolated to one file on day one, not later:
App\Nova\Filters\DependentSelect extending the package base class.Filter.parent() and a protected options builder, so a Nova break is a single-file fix.This wrapper is the exit plan made concrete. Swapping to a hand-rolled implementation becomes a one-file change, which is what makes adopting a 3-star package defensible at all.
Compatibility. Two guardrails, both mandatory:
php artisan nova:publish --force to the CI/deploy pipeline. This is not optional housekeeping; skipping it is the classic silent failure where filters render but options never refresh.Optionally fork under the org account before rollout rather than after the first breakage. Forking later means waiting on someone else's timeline for a patch you already need.
Sequencing.
composer.json; confirm Nova major, PHP floor, asset delivery, and option-fetch strategy. If any mismatch, stop.blank($parent) ? return [] : ...) from the first commit, and route options() through the same policy scoping as the index query.Maintenance. The real cost of this package is ownership, not license. Assume full responsibility for the code the moment it is in composer.json. Maintenance load has three components: a per-Nova-major patching budget (roughly a day each), ongoing review of the wrapper as resources adopt the pattern, and the standing cost of tracking an upstream that may never publish again. Confining the dependency to one abstract class is what keeps this number small; spreading direct inheritance across a dozen filters is what makes it balloon.
Support. Support load comes from a small set of recurring, diagnosable complaints, and the mitigation is a documented decision tree rather than heroics:
nova:publish did not run for the current deploy.apply() or document the reset path.Publish the debugging checklist and the URL-state behavior to the support team. The cost of an undocumented cascade is measured in triage time.
Scaling. Scale risk is query-shaped, not traffic-shaped — admin tooling has low concurrency but can hit large tables. Two controls: the empty-parent guard (return [], never the full set, never an unbounded pluck) and short-lived caching of option lists keyed by parent ID and tenant, with a one-minute TTL that is entirely adequate for admin workflows. Both are implementation responsibilities, not package features — the package will not protect against either by default. Multi-tenant deployments need tenant in the cache key and, more importantly, in the authorization scoping.
Failure modes. Ranked by blast radius:
options(). Highest severity; the only failure with a security dimension. Mitigate by policy scoping from the first commit.Ramp-up. A capable Laravel/Nova engineer gets productive in under a day given the wrapper already exists — the mental model is small (base class, parent(), options(), registration order). The onboarding artifact that matters is not the README, which is thin, but two internal documents: the debugging checklist and the extension-point boundary. Treat the base class options(), the parent() declaration, and any parent-change hook as the only stable surfaces; everything else is private and should be treated as such. Ramp-up cost is front-loaded into that documentation and repaid the first time someone other than the original author has to debug a cascade.
Boundary decision — make it explicit and in writing. This should not become load-bearing infrastructure for revenue-, support-, or finance-critical workflows. That is a product decision, not an engineering one, and it should be recorded before rollout alongside the named owner, the pinned Nova version, and the exit plan. The package pays off when the pattern repeats across several Nova resources and a single-major Nova pin is acceptable; it does not pay off for one screen, and it never pays off as a platform dependency.
How can I help you explore Laravel packages today?