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

Nova Multiselect Filter Laravel Package

optimistdigital/nova-multiselect-filter

A Laravel Nova package that adds a multiselect filter to resource indexes, letting users filter records by multiple options in a single filter. Configurable options and simple integration for Nova admin panels.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This package is a thin, single-purpose extension of Laravel Nova's native filter contract. It substitutes one class (Outl1ne\NovaMultiselectFilter\MultiselectFilter) for Nova's Laravel\Nova\Filters\Filter and adds exactly one behavior: the filter UI accepts multiple selected values and hands them to apply() as an array instead of a scalar. Everything else — registration in filters(), serialization into the index query string, execution against resource index and count queries — is stock Nova machinery. There is no service provider, no config file, no migration, no asset build step; the Vue component ships precompiled. Architecturally this is about as contained as a dependency gets: it lives entirely in the admin presentation layer and is inert outside Nova's request cycle.

Integration feasibility. Low effort, well-bounded. The implementer extends the package class, implements options() (a key→label map) and apply() (a query constraint), and registers the filter on a resource. Typical work is a few hours for the first filter and minutes for each subsequent one. The genuine friction is not code volume but naming: the vendor has moved from optimistdigital/nova-multiselect-filter to outl1ne/nova-multiselect-filter, and most search results still show the old namespace. That mismatch produces a failure mode where Composer succeeds but the use statement is unresolvable — worth stating in the ticket so the implementer verifies both the Composer name and the PHP namespace from vendor/.

Technical risk. The risk is concentrated in two places, both cheap to test and expensive to discover late:

  1. Silent render failure across Nova majors. Nova's asset pipeline has broken third-party filter components before, and the failure is quiet — the filter either doesn't appear or appears and never fires. A green composer require proves nothing. This must be a manual smoke test on a real index after install and after every Nova upgrade.
  2. The empty-selection trap. If apply() passes an empty array into whereIn, MySQL returns zero rows, the admin sees an empty table, and the reasonable conclusion is that the app lost their data. Every filter needs an explicit guard returning the query unchanged when nothing is selected.

Secondary risks: option keys come back as strings (fine for MySQL implicit casting, a problem on Postgres or UUID primary keys); whereIn excludes NULL rows, so an "unset" bucket needs an explicit sentinel; and filter key() values leak into shareable URLs, so renaming a filter class silently breaks bookmarked views. A "select all" over a large table can also generate a pathological whereIn with thousands of bindings.

Key questions.

  • Are we on a Nova major this package explicitly supports, and has someone confirmed the component actually mounts on a test index?
  • Does Nova's built-in SelectFilter already cover multi-select in our version? That would be the cheapest possible outcome and should be checked before anything is installed.
  • Who owns this dependency at upgrade time — one named person who re-verifies rendering on each Nova major?
  • Is this the first outl1ne package in our stack, or does the vendor relationship already exist? Marginal cost differs sharply between those cases.
  • Does the lockfile pull in the sibling nova-multiselect-field? If so, we've onboarded two Nova-major compatibility windows rather than one.
  • How large is the largest option set we intend to support? Options are not searchable server-side — the client-side search only filters what's already loaded — so this package is the wrong tool for option sets that can't fit in a dropdown.

Integration Approach

Stack fit. Assumes Laravel Nova, which is itself an assumption worth confirming before anything else. The package is MIT-licensed, PHP-only from the consumer's perspective, and requires no Node toolchain, no published assets, and no queue or scheduler involvement. It touches no models, migrations, routes, auth, or jobs. That means it can be adopted on a legacy Nova install without dragging in unrelated upgrade work — a meaningful property when the alternative (building a local filter) costs roughly a day and creates permanent in-house ownership of the same code.

Migration path. Three steps: composer require outl1ne/nova-multiselect-filter, add the filter class extending MultiselectFilter, add one line to the resource's filters(). The removal path is exactly as short — composer remove, delete the filter class, delete one line from filters(). That reversibility is the strongest argument for adoption: the decision can be made and unmade without ceremony. If any of those three removal steps turns out to be hard, something has leaked out of the admin layer into the application and should be fixed before it calcifies.

Compatibility. Two constraints to pin down before committing:

  • Nova major. Verify the package's declared Nova constraint resolves against the installed version; composer why-not outl1ne/nova-multiselect-filter <nova-version> is the diagnostic when a Nova bump stalls.
  • The $values payload shape. Depending on the underlying field component version, selections may arrive as a list of keys or as a keyed key => label map. This should be verified empirically once (a debug log in apply() on first run) and recorded in a code comment, with defensive handling if it turns out to be map-shaped.

Pin an explicit version constraint rather than a floating ^x, and commit the lockfile. MIT licensing means forking is legal and inexpensive if upstream goes quiet — a real mitigation given 46 stars and zero dependents.

Sequencing.

  1. Confirm the gap. Check whether Nova's built-in SelectFilter already handles multi-select. If it does, stop.
  2. Spike on one low-traffic admin index — support tickets by status, orders by payment state, users by role. Verify the dropdown actually mounts in the browser, not just that Composer exited zero.
  3. Validate query semantics with unit tests on apply() directly — instantiating the filter with a NovaRequest and asserting bindings, plus explicit cases for [] and null. No browser needed.
  4. Roll out to the recurring pattern across the other resources where multi-value filtering is the actual need.
  5. Only then consider the relationship-backed variant (whereHas, cached options), which carries the extra operational concerns below.

Deferring the expensive "advanced filtering / saved views / query builder" initiative is the point of this sequencing: adoption here buys a visible productivity win now, and usage data from these small filters becomes evidence for whether the larger investment is justified.

Operational Impact

Maintenance. The recurring cost is one line in the lockfile and one item on every future Nova-major upgrade checklist. That is genuinely small — but it should be a deliberate decision with a named owner, not a drive-by composer require that surfaces in an audit six months later. The code the team nominally owns is about 40 lines of contract (options(), apply(), column(), key()) plus whatever business logic gets written into each filter. Keeping that business logic out of the filters matters: the definition of "which statuses count as open" belongs on the model or in a query scope, not in a Nova filter class. The moment a filter becomes load-bearing for reporting, it stops being removable in one line — and removability is the entire reason to accept a low-adoption dependency.

Support. Internal-staff audience, low polish expectations, and a narrow failure surface. The first-line diagnostic is the browser Network tab, not the query: a filter that "does nothing" is almost always a component that failed to mount. That means support burden concentrates on rendering, not on data correctness — provided the empty-selection guard is in place everywhere. Without that guard, the support story is much worse, because the symptom looks like data loss.

Scaling. Three ceilings worth naming:

  • Option set size. Options render into a dropdown and search is client-side only; there is no async or paginated loader. Constrain option lists — top N by usage, or a scoped subset — rather than dumping a 50k-row table.
  • whereIn width. A "select all" can generate thousands of bindings and, at the extreme, exceed max_allowed_packet. Same mitigation: bound the option set.
  • options() cost. It runs on every index render and again on filter apply, and filter apply() also executes against count queries. For relationship-backed options, Cache::remember around the pluck() and busting on the model's saved/deleted events is the single highest-leverage change — and keeping apply() cheap matters because count queries hit it too.

None of these are blockers for the intended use case (a handful of static enum values, or a modest relationship list). They define the boundary at which this package stops being the right tool.

Failure modes. Ranked by how bad they look to a user:

Failure Symptom Mitigation
whereIn([]) reaches the query Empty table; user believes data was lost Guard every apply(); unit-test [] and null
Component fails to mount after Nova upgrade Filter does nothing, silently Smoke-test on a real index as a release checklist item
Vendor namespace drift Composer installs, use unresolvable Verify both names from vendor/
String-typed IDs on Postgres/UUID PKs Wrong or failing matches Cast explicitly where the column type is strict
NULL rows never match "Unset" records vanish from filtered views Explicit __null sentinel option, translated in apply()
key() renamed Bookmarked filtered URLs break Keep the old key as an alias for a release

The two failure modes that actually generate escalations are the first two — one looks like data loss, the other looks like the feature was never built. Both are eliminated by cheap, specific practices: an empty-array guard in every filter, and treating "component mounts" as a verification step rather than an assumption.

Ramp-up. Very fast, with one prerequisite. The implementer needs to read exactly two things: vendor/outl1ne/nova-multiselect-filter/src/MultiselectFilter.php (the whole contract is ~40 lines) and the README's usage block. Everything beyond that is Nova filter machinery the team already knows. The prerequisite is Nova familiarity — this is not a package to hand to someone who hasn't written a Nova filter before, because the failure modes are silent and the debugging instinct (check the browser, not the query) is learned rather than obvious.

Once three of these exist, extract a base class or a FiltersByEnum trait. That kills the copy-paste and gives one place to fix the empty-selection guard. Ramp-up for the fourth filter should be measured in minutes.

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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log