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

Dependent Filter Laravel Package

okol/dependent-filter

Laravel package for dependent, cascading filters: options in one filter update based on selections in another. Simplifies linked dropdowns and dynamic query filtering in admin panels and search forms, with a simple API for defining filter relationships.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require okol/dependent-filter

The package is a Nova filter extension, so there is no publish step for config in the typical case — read the README's install section first and check composer.json for the Nova version constraint it declares. Anything pinned to laravel/nova: ^4 will not load on a Nova 5 install, and vice versa.

Where to look first

  1. README.md — specifically the example filter, since the whole value of the package is the parent/child contract.
  2. src/ — the abstract filter class or trait you are expected to extend; that is the surface you will actually code against.
  3. resources/js/ — the Vue filter component. Nova renders custom filters from JS, so this file determines whether the child filter re-fetches its options when the parent changes. If it doesn't, you have a UI problem the PHP side can't fix.
  4. Your own app/Nova/Filters/ directory — the first thing you'll write is a subclass of whatever base class the package ships.

First use case: Country → City

Register both filters on a resource:

// app/Nova/Resource.php or the specific Resource
public function filters(Request $request)
{
    return [
        new Filters\CountryFilter,
        new Filters\CityFilter,
    ];
}

Then the child filter narrows its options to the parent's current selection, and the query only applies the child constraint when it is set. That is the entire day-to-day story: the parent drives the child's option list, the child drives the query. Everything else in this package is plumbing around that.

Start with the smallest possible case — one parent, one child, on one resource — and confirm the child's dropdown actually repopulates in the browser before you build a three-level cascade.

Implementation Patterns

The child declares its parent, then reads the parent's value

The pattern you will write most often looks like this (names adapted to the package's actual API — check the README for the exact method signatures):

class CityFilter extends DependentFilter
{
    public function dependsOn(): string
    {
        return CountryFilter::class;
    }

    public function options(Request $request)
    {
        $countryId = $this->parentValue($request, CountryFilter::class);

        if (! $countryId) {
            return []; // no parent selection -> nothing to choose from
        }

        return City::where('country_id', $countryId)
            ->orderBy('name')
            ->pluck('name', 'id')
            ->all();
    }

    public function apply(Request $request, $query, $value)
    {
        return $query->where('city_id', $value);
    }
}

Two rules that pay for themselves immediately:

  • options() returns [] when the parent is unset. An empty dropdown is honest; a full unfiltered dropdown invites the user to pick a city the query will then silently exclude.
  • apply() must be a no-op when $value is null. Nova calls apply() with whatever is in the filter state, including stale values from a bookmark.

Three-level cascades

Region → Country → City works, but treat it as a chain of dependent filters rather than one filter with a level switch. Each level declares exactly one parent, and each reads only its immediate parent's value. Reading a grandparent's value directly creates a hidden coupling that breaks the moment a user reorders or removes a filter.

Persisting and bookmarks

Nova encodes filter state into the URL, so a bookmarked index page restores the whole chain. This is where dependent filters shine: the bookmark restores country=42, city=7, and both apply() calls fire without the user re-picking the country. Test this explicitly — it is the highest-value behaviour and the most likely to be broken by a stale-value bug.

Combining with lenses and metrics

Anything that runs a filter chain — index views, lenses that respect filters, metric cards — benefits. Anything that constructs a query directly, such as a custom card or an export job, does not. If you have an export action, do not assume the active filters are applied; pass the selected IDs explicitly or re-run the filter chain yourself.

Reusable base classes

If the package's base class is thin, wrap it once in app/Nova/Filters/BaseDependentFilter.php and have your filters extend that instead. When the package changes or you need to swap it out, you change one file rather than every filter in the app. With a zero-star dependency this is not paranoia, it's basic hygiene.

Gotchas and Tips

Filter state arrives encoded. Nova serialises filter values into the query string, and the parent's value is not sitting at a friendly $request->country. Whatever helper the package gives you for reading the parent is doing decoding for you — use it rather than reaching into the request directly. If parentValue() returns null even though the parent is visibly selected, dd($request->all()) inside options() is the fastest way to see the actual shape.

options() is not reactive by default. In stock Nova, a filter's options are fetched when the filter panel opens. A dependent child filter needs its component to re-request options when the parent's change event fires. If the package's Vue component doesn't do this, the child stays stale until the panel is reopened — and no amount of PHP will fix it. Verify this in the browser with the network tab open on the first day, not the first week.

Empty options can look like a bug. If the parent filter is registered after the child, or isn't in the resource's filters() at all, the child has nothing to read. Registration order in the array is not always irrelevant to the UI ordering; check it early.

Cache your option lists carefully. A dropdown that hits the database on every panel open is fine for 20 cities and painful for 20,000. Cache with a key that includes the parent ID, and forget the cache when the child table changes. Do not cache the "no parent selected" case across parents.

Stale child values after the parent changes. When the user switches country, the previously selected city may remain in the filter state and get applied to the query. Explicitly clear the child value in the Vue component when the parent changes, or guard in apply() with a whereHas-style check that the child actually belongs to the parent.

Package maturity risk. This package has effectively no adoption. Pin the exact version in composer.json, read src/ once before relying on it in production, and keep the base-class wrapper described above. If the package turns out to be a thin wrapper over a dependsOn() convention, you may find reimplementing it as a trait in your app is less work than maintaining the dependency.

Debugging checklist. In order: (1) dd($request->all()) in options() — is the parent value present and decoded? (2) Network tab — does opening the filter panel trigger a request for the child's options? (3) Is the child's Vue component actually the one from this package, or did Nova fall back to a default? (4) Does the query behave correctly when you set the filter values manually in the URL? Isolating PHP from JS halves the search space quickly.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings