drunken-elf/nova-global-filter
A Laravel Nova package that adds a persistent global filter bar to your admin panel, letting you apply and retain filters across resources for faster navigation and consistent, focused views of your data.
This is a single-maintainer, low-adoption package (1 star, 0 dependents), so treat the source as the documentation. First step is always to read vendor/drunken-elf/nova-global-filter/src and the README before wiring it into anything you care about.
Minimal setup:
composer require drunken-elf/nova-global-filter
Then check whether the service provider is auto-discovered (drunken-elf/nova-global-filter should appear in bootstrap/cache/packages.php, or you add it to config/app.php for older Laravel). Nova packages usually hook in via Nova::serving() — look for that in the provider. If there's a publishable config or asset, run:
php artisan vendor:publish --provider="DrunkenElf\GlobalFilter\GlobalFilterServiceProvider"
php artisan nova:publish
Where to look first: the package's src/ directory, any resources/js files (this almost certainly ships a small Vue component or extends Nova's filter dropdown), and the README's usage example. If the README is thin, grep the source for Filter, Request, and apply( to understand the mechanism.
First use case: take a filter you already have on a resource — e.g. a DateRangeFilter or a TeamFilter — and put it on a dashboard instead of duplicating the query logic inside a Trend/Value metric. Concretely:
php artisan nova:filter DateRangeFilter.filters() or via a global registration call).calculate() reflects the filter value when you change it in the UI.Get that one end-to-end path working before adding more filters.
Reuse over rewrite. The value proposition is that the filter class is unchanged. Keep your existing Filter subclasses with their apply(Request $request, $query, $value) and options(Request $request) implementations intact — the package's job is to resolve which filter values are active for a given card request and hand them to your query.
Dashboard cards. The typical pattern is that your metric's calculate() (or the package's card base class) receives the resolved filter value and applies it to the query builder before aggregating:
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class)
->when($request->input('filters'), function ($query, $filters) {
// delegate to the filter's apply() rather than reimplementing
return $query->where(/* resolved filter constraint */);
});
}
If the package exposes a helper or trait for this, prefer it over hand-rolling input parsing — that's the whole point of the dependency. If it doesn't, the package is small enough that copying the resolution logic into a project trait may beat taking the dependency.
Index / Detail views. On the index screen, global filters sit alongside the resource filter dropdown. The practical pattern is to register filters that make sense across all resources on the page (date ranges, ownership, status) globally, and keep resource-specific filters in filters(). On detail views, filters generally constrain related-resource queries or resource counts in the same way.
Integration tips:
TeamFilter on a finance dashboard is noise on an ops dashboard.Audit before adoption. 0 dependents and 1 star means no community pressure-testing. Read the source for:
apply() signatures and the request shape for filter values shift between versions).nova:publish / npm run dev.Nova::script() / Nova::style() calls that could conflict with your existing asset pipeline.Cache keys are the classic trap. Nova metric cards support cacheFor(). If a card is cached, changing the filter value won't move the number unless the cache key incorporates the filter value. Either disable caching on filtered cards or include the resolved filter hash in the key. This is the #1 source of "the filter doesn't work" bug reports with any global-filter approach.
Filter value serialization. Nova encodes filter selections into the request under keys derived from the filter class (and often base64-encoded values). Don't assume $request->input('filters') is a plain array of scalars. Use the package's decoder if it exposes one, and test with multi-select and date-range filters specifically — those are where encoding assumptions break.
Authorization. A filter's canSee() is normally evaluated in the context of the resource's filter dropdown. Global registration may bypass or duplicate that check. If a filter leaks data via a card's aggregate (e.g., a manager-only team filter), verify canSee() still gates it on dashboards.
Debugging approach: dump the resolved filter payload at the top of calculate() with logger()->info(...) and compare it to what the filter dropdown sends in the request payload (browser devtools → Network → the card's refresh request). If they diverge, the issue is in the package's resolution layer, not your query. Also confirm the card request is a Nova metric request and not going through a cached dashboard payload.
Extension points worth knowing: publishable config/views (if present), the card base class or trait (subclass it to add your own default filters), and the service provider's Nova::serving() hook for registering assets.
Alternatives if it doesn't hold up: the pattern is small enough to implement as a trait in your app — parse the filter payload off the NovaRequest, call each filter's apply() against the metric query, and you have the same feature without an unmaintained dependency. Given the dependency's adoption profile, budget for that fallback rather than assuming long-term maintenance.
How can I help you explore Laravel packages today?