reedware/nova-text-filter
A Laravel Nova filter that adds a free-text search field to your resource index, letting users filter records by typing keywords across selected columns. Lightweight, configurable, and easy to drop into any Nova resource for quick, intuitive searching.
Architecture fit. This is a leaf dependency on the admin surface, not a platform component. It ships a Nova filter contract implementation — an abstract PHP filter class plus a Vue input component — that plugs into Nova's existing filter lifecycle. It adds no search service, no index, no new data store, and no write path. That makes it architecturally cheap to adopt and cheap to remove. It fits only when the admin surface is already Nova, the target data is Eloquent-backed (including relationships and qualified columns), and the need is column-targeted search alongside Nova's global search. It does not fit any requirement for faceted filtering, saved views, cross-model search, or high-volume ranked search — those are a different architecture (Scout/Meilisearch/Postgres full-text), not a bigger version of this.
Integration feasibility. The happy path is short and well understood: composer require, provider auto-discovery, nova:filter scaffolding, reparent to the package base class, register in filters(). A competent Laravel developer gets a working filter in hours, not days. The real cost is verification, not construction: confirming the installed Nova major matches the package's composer.json constraints, confirming the apply() signature (NovaRequest vs. the older Request — a mismatch is an immediate fatal), and confirming the frontend component actually mounts after asset publishing. Feasibility is high; certified feasibility requires a staging spike against the exact Nova/Laravel/PHP/driver matrix you deploy.
Technical risk. Two distinct risk classes, and they should be tracked separately.
LIKE '%%' full-table scans, unescaped %/_ in user input silently broadening matches, ungrouped orWhere clauses OR-ing past authorization scopes and soft deletes, driver-dependent case sensitivity (MySQL _ci collation vs. Postgres LIKE/ILIKE), and leading-wildcard LIKE being unable to use a B-tree index. Every one of these is preventable with a shared internal base class — which is the single highest-leverage decision in this assessment.Key questions.
apply()?nova-text-filter-assets / nova:publish) exist in the deploy pipeline, and is public/vendor/ artifact handling covered?Stack fit. Laravel + Nova only; if the admin surface isn't Nova, this package provides nothing and the assessment ends. The fit is strongest where the team already extends Nova filters and has a place to put shared filter base classes (App\Nova\Filters). The package's own surface should stay small: one internal abstract class, concrete filters of a few lines each, and no logic in the package subclass itself.
Migration path. Where no filter exists today, adoption is purely additive — no data migration, no schema change, no backfill, no user-facing cutover. Where it replaces existing custom filter classes, treat it as a behavior change: run old and new in parallel in staging, diff result sets on representative records, and compare query plans before switching. The absence of a migration burden is precisely why this is a low-cost probe rather than a project.
Compatibility. Verify, don't assume. Pin the exact package version, commit composer.lock, and re-run the spike against the deployed matrix. Three concrete checks: (a) the apply() signature against the installed Nova major; (b) the component property on the base class against what actually compiles into public/vendor/ after a Nova upgrade; (c) case-sensitivity behavior settled once in the base class against the production driver, not the developer's local MySQL.
Sequencing.
addcslashes escaping (with explicit ESCAPE where the driver requires it), grouped orWhere closure for multi-column matches, and a match-mode hook (contains / starts / exact) so semantics aren't hardcoded per filter.Maintenance. Low volume, non-zero obligation. The package is stable but unmaintained-by-default; the internal base class is the real asset and the real maintenance surface. Name an owner in the decision record, note the license, and define the fork-or-replace trigger in advance. Mitigation cost is genuinely small — the replacement is a small in-house Nova filter — so the governance burden should be proportionate, not ceremonial.
Support. Community only, no vendor escalation path. Support therefore resolves internally by design, which is acceptable here precisely because the abstraction layer sits in your codebase. The knowledge concentration risk (one developer understanding the apply()/component coupling) should be addressed by keeping the base class small and reviewed.
Scaling. This is the binding constraint and a design decision, not a tuning exercise. Leading-wildcard LIKE cannot use a B-tree index, so the filter belongs on modest admin tables. Agree the row-count threshold before rollout and document which resources are permitted to use it. When volume grows, keep the identical TextFilter UI and route the query inside apply() to Scout, Meilisearch, or Postgres full-text — the UI contract survives the backend swap, which is what makes this a safe first step rather than a dead end.
Failure modes and detection.
| Failure | Detection | Mitigation |
|---|---|---|
Blank input → LIKE '%%' full scan |
Slow index loads when filter is "empty" | blank() short-circuit in the base class |
Ungrouped orWhere leaks past auth/soft-delete scopes |
Records visible that shouldn't be — often discovered late | Grouped closure from day one |
Unescaped %/_ in input broadens matches |
User-reported "wrong results" | addcslashes, explicit ESCAPE on Postgres/SQL Server |
| Case-sensitivity drift MySQL → Postgres | Works locally, differs in production | Settle ILIKE/lower() once in the base class |
| Component fails to mount after Nova upgrade | Raw input or filter panel error | Publish assets; verify component name against compiled asset; staging revalidation |
apply() signature incompatibility |
Fatal "Declaration must be compatible" at deploy | Pre-flight compatibility check on the Nova major |
| Upstream abandonment | No releases since 2023 | Version pin, lockfile commit, pre-agreed fork/replace |
Ramp-up. Developer onboarding is measured in hours: the package is small, the README plus the abstract class in src/ answer the three questions you can't safely guess (component name, apply() signature, matching hooks). The actual learning curve is the gotcha list above, which is why it should live in the internal base class and its docblock rather than in individual filters or tribal knowledge. Frame the whole adoption to stakeholders as internal productivity tooling with a bounded blast radius — an admin-panel filter, not a search roadmap — and it stays correctly scoped against customer-facing priorities.
How can I help you explore Laravel packages today?