nemrutco/nova-global-filter
Nemrutco Nova Global Filter adds a configurable global filter to Laravel Nova, letting you apply shared filtering across resources and streamline admin list views. It integrates with Nova’s filter system for quick, consistent data exploration.
Check your Nova version first — this is the whole decision. The last release is v1.3.0 (2023-03-08) and the only thing it claims is "Nova 4 support." The upstream repo is archived. On Nova 5+, don't install it; read it as a reference implementation and port the pattern.
Pin the version, don't float it.
composer require nemrutco/nova-global-filter:1.3.0
Then open vendor/nemrutco/nova-global-filter/composer.json and confirm the laravel/nova constraint actually covers the minor you're on. The changelog doesn't say which Nova 4 minors were exercised, so the constraint is your only signal.
Read the three files before you wire anything. The package is small: a trait that resolves filters per resource, a service-provider registration that broadcasts them, and an inline Vue component that reuses Nova's filter UI. Ten minutes tells you whether it hooks Nova internals (fragile on every Nova bump) or public extension points (portable). That determines whether your next upgrade is a constraint edit or a fork.
Register the filters once, at the provider level. Define the canonical set in NovaServiceProvider and have resources resolve their filters() from that same source. If you duplicate filter classes across filters() methods, you've recreated the drift problem the package exists to remove.
Smoke test with four consumers on one page. Take one date-range filter, put it on a dashboard with two metrics, one custom card, and one resource table, and confirm all four move together on the same request. That is the only test that matters, because the failure mode is silent (see Gotchas).
Know where "will this work on my dashboard" is actually decided. It's the Vue component — specifically whether it reads filter state from the query string or from component/session state. Check that before you promise anyone a bookmarkable dashboard.
If you only need filter state on a single card, skip the package entirely. A request-parameter read inside that one card is cheaper to own than an integration with an archived dependency.
One definition, many consumers. The canonical filter set lives in NovaServiceProvider; every resource's filters() returns from it. Changing the date-range filter becomes a one-file diff instead of an N-resource audit. This is the pattern the package exists to enable — use it that way.
Filter-once, scope-everywhere dashboards. The strongest day-to-day use case is an ops/support console: a tenant or date-range filter registered globally, resource tables consuming it through Nova natively, and metrics/custom cards reading the broadcast state. Budget explicit time for a parity check — the table and the KPI cards must agree on the same request, not merely look plausible.
Cards read state; they don't re-declare it. In a card's calculate(), pull the value out of the request the same way the package's Vue component does:
public function calculate(NovaRequest $request)
{
$range = $request->input('date_range'); // confirm the exact key the component broadcasts
return $this->count($request, Order::whereBetween('created_at', $range));
}
Never add a second filter control inside the card body. Two controls on one page is exactly how you produce two different numbers on the same dashboard, with no way for the user to tell which is correct.
Apply the trait selectively for opt-in. If only some resources should inherit the global set, apply the trait to those resources rather than rewriting every filters() method. Keeps the diff reviewable and the blast radius small when you're validating behaviour.
Validate composition, not the happy path. Before rollout, test in combination: a global date range alongside a locally defined relationship filter, a lens, and a soft-delete-scoped resource. Global plus resource-specific filters in the same bar is where ordering, deduplication, and "which one wins" surface — and with zero dependents, nobody has stress-tested that for you.
Vendor it if you're staying on Nova 4. Copy the trait, the provider registration, and the Vue component into app/Nova/Concerns (or a private package), strip the upstream namespace, keep the MIT attribution, and put it in CODEOWNERS. You get the pattern without inheriting an unmaintained dependency, and you can patch it the moment Nova moves.
Test the propagation contract, not the package. Write a feature test that hits the dashboard with a filter applied and asserts the metric's scope. Then write the inverse — a card that ignores the filter — and make CI fail on it. That second test is your entire safety net; everything else is convenience.
The dominant failure mode is silent. A metric that ignores the broadcast filter renders a plausible, unscoped number on exactly the surface where someone makes a decision. No 500, no warning, no log line. Assume any new card is unfiltered until proven otherwise — and prove it with a test, not a screenshot.
Zero dependents means you are the edge-case discovery team. Lenses, relationship filters, multi-filter composition, soft deletes, policy-scoped resources: none of it has a production population behind it. Treat every one of those as untested until you've personally tried it.
Filter state location decides whether dashboards are shareable. Query-string state means links are shareable and correct on refresh. Session or component state means a shared link silently shows different numbers to different people. Check the Vue component before you promise anyone a bookmarkable dashboard.
Filters are UI convenience, not authorization. A global tenant filter scopes what a user sees, not what they may access. Scope enforcement belongs in the query/policy layer — a user who strips a query parameter must not gain data. Don't let a tidy filter bar create the impression that scoping is enforced.
Date filters carry timezone and boundary semantics. "Last 30 days" computed inside the card and inside the resource query can differ by a day boundary and by timezone. Assert both against the same fixture data; don't eyeball the chart.
Watch for duplicate filter bars. If a resource defines its own filter and inherits the global set, you can render two controls with the same label. Audit filters() on every resource you touch, especially ones with pre-existing local filters.
Debug in this order: dump the raw request payload on the card's endpoint → confirm the filter key is present → confirm the card's query actually reads it → only then look at the Vue component. Most "it isn't filtering" reports stop at step one, and step four is almost never the culprit.
Stale asset/cache trap. After changing provider-level filter registration, run nova:clear-cache and re-cache routes if you cache them. The inline Vue component compiles into your Nova assets, so a stale build is a common cause of "the filter bar didn't change."
Keep it in the Nova upgrade runbook. When you bump Nova, add two explicit steps: re-run the propagation test, and re-read this package's composer.json constraint. A repo that was archived, briefly revived for Nova 4, and is archived again can go dormant permanently — you're on a clock you don't control.
Extension points are thin by design. Need custom serialization, per-card filter subsets, or cross-page persistence? Fork rather than fight it. There's no maintainer to take a PR, and the surface is small enough to own outright — which is the recommended path even on Nova 4.
How can I help you explore Laravel packages today?