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 Eloquent Searchable Laravel Package

webard/nova-eloquent-searchable

A Laravel Nova package that adds Eloquent-powered searchable fields and relationships to your admin panel, letting users quickly find related records with fast, query-backed search instead of long dropdowns.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify compatibility before anything else. This package has a single release (2024-07-23), one star and zero dependents, so treat the first 15 minutes as a compatibility audit rather than an install:

composer require webard/nova-eloquent-searchable

Then immediately open vendor/webard/nova-eloquent-searchable/composer.json and read the require block. With a package this small, the constraint range is the single best predictor of whether it will work in your app — you want to see an explicit Nova major (laravel/nova: ^4.0) and a Laravel range that includes your version. If the constraints are loose (* or ^10|^11) rather than tested, assume untested and plan to smoke-test everything in the Gotchas section.

Where to look first, in order:

  1. composer.json — supported Nova/Laravel versions (see above).
  2. src/ — expect a trait (applied to a Nova Resource) plus a ServiceProvider. The provider tells you whether anything is registered globally (macros, config, bindings) versus everything being opt-in per resource.
  3. The companion webard/eloquent-searchable package, if it is pulled in. In this family of packages the model owns the search definition and the Nova package is a thin adapter that routes Nova's search box into that definition. Understanding which half does what is the whole mental model.
  4. README last — for a package this young the source is more reliable than the docs.

First use case — the one that justifies the dependency. Nova's default resource search compiles to WHERE col LIKE '%term%' across whatever you list in public static $search. On a table past a few hundred thousand rows that is a full scan on every keystroke, it can't rank results, and it can't reach into related tables or a real search index. The payoff here is keeping the Nova UI untouched while the query underneath becomes whatever you want: a full-text index, a Scout/Meilisearch lookup, a multi-column weighted match, or a whereHas across relations.

Minimal path:

  1. Define the search behaviour on the Eloquent model (per the companion package's contract).
  2. Apply the package's trait to the corresponding Nova Resource and remove or slim down public static $search.
  3. Seed ~50k rows, search in the Nova UI, and confirm via DB::listen() that the generated SQL is the query you intended — not a LIKE scan.

If step 3 doesn't produce the query shape you expect, stop and evaluate before building on it. At this maturity level, "works on the demo model" is the only guarantee you get.

Implementation Patterns

Keep search logic on the model, Nova as a thin caller. The durable pattern with any Nova search adapter is: the model exposes a scope or searchable definition, the Nova resource only wires it up. That way the same search backs your API, your artisan commands and your tests, and swapping Nova out later costs you one trait.

// app/Models/Article.php — the real logic lives here
public function scopeSearch(Builder $query, string $term): Builder
{
    return $query->whereFullText(['title', 'body'], $term);
}
// app/Nova/Article.php — Nova just delegates
use Webard\NovaEloquentSearchable\...; // confirm exact trait name in src/

class Article extends Resource
{
    use Searchable; // routes Nova's search box to the model

    public static $search = ['id']; // keep non-empty; see Gotchas
}

Test the model, not the package. You cannot easily boot Nova in CI without a license, and you shouldn't be asserting a third-party package's behaviour anyway. Unit-test Article::search('term')->toSql() and its results; treat the Nova wiring as a two-line integration you verify manually once. This also means a future fork or replacement is a zero-test-churn change.

Scope everything to authorization. A custom search that reaches across relations or into an external index bypasses the assumptions Nova's default per-column search makes. Nova checks authorizedToViewAny on the resource, but it does not re-check row-level visibility inside your custom query. If you have tenant scoping or per-user visibility, apply it inside the search scope, not in the resource:

public function scopeSearch(Builder $query, string $term): Builder
{
    return $query
        ->whereBelongsTo(auth()->user()->team)   // never rely on Nova for this
        ->whereFullText(['title', 'body'], $term);
}

Handle the empty term explicitly. Nova fires the search path with an empty string in several situations (clearing the box, initial load in some versions). Returning an unbounded result set there is how you accidentally SELECT * on a million rows every page load. Early-return the base query when trim($term) === ''.

Prefer relevance ordering only when the user hasn't sorted. Nova's index lets users click column headers to sort. If your search always appends ORDER BY relevance, you either fight the user's sort or silently ignore it. Detect whether a sort was requested and only apply relevance as the default:

if (! request()->has('orderBy')) {
    $query->orderByDesc('relevance');
}

Queue your index writes if you use Scout. For a Scout/Meilisearch-backed implementation, enable searchable() via observers with queue on so a bulk import or a Nova inline edit doesn't block the request. This is the main operational difference between this approach and plain LIKE — you gain speed and lose instant consistency.

Gotchas and Tips

An empty $search array hides the search box entirely. Nova decides whether to render the search input based on the resource's searchable columns. If the package moves search responsibility elsewhere and you blank out $search, users lose the UI, not just the default behaviour. Always leave at least ['id'] in place until you've confirmed the search box still appears.

Nova 5 is almost certainly untested. The last release predates Nova 5. Nova's resource search surface changed between majors, so verify on a branch, not in main. If it breaks, the fix is usually small (a method signature or a $search resolution path) — fork it, patch it, and point Composer at your fork with a VCS repository entry. With one star and zero dependents you should assume nobody else will report the bug you hit.

Pin the version and vendor-review the source. There is exactly one release and no dependents, meaning no downstream has exercised this code in anger. composer require webard/nova-eloquent-searchable:0.1.* (whatever the exact tag is) rather than ^0.1, and read every line of src/ once. It's a small package; an hour of reading buys you certainty about what SQL it emits.

Beware unparameterised raw SQL. Full-text search implementations frequently reach for whereRaw('MATCH(...) AGAINST(?)'). Check that the user-supplied term is bound, not interpolated. Search terms come straight from a query string, so an interpolated term is an injection vector that Nova's default LIKE search would never have exposed. Grep the source for whereRaw and DB::raw.

Stale results right after save. With any external index (Scout, Meilisearch, Algolia), a record saved via a Nova form may not appear in search for a second or two. Users read this as data loss. Either document it, or in the search scope fall back to a direct DB lookup for exact ID matches so "I just created it, where is it?" resolves instantly.

Debug the actual query, not the intent. Wrap a local test in a listener and watch what Nova really runs:

// AppServiceProvider::boot()
DB::listen(fn ($q) => logger($q->sql, $q->bindings));

If you see like ? with %term% bindings, the package is not intercepting the search and you're still on the default path — the trait isn't applied, is applied to the wrong class, or the service provider isn't registered (check bootstrap/providers.php / config/app.php).

Extension points are limited, so own the seam. With a package this small there is likely no config file and no published stub. Your extension point is the trait method itself — override it in your resource, or better, define your own trait that wraps theirs. That keeps the eventual migration to a maintained alternative (or a 30-line in-house replacement) confined to one file.

Cost the fallback before you commit. The honest calculus: this package saves you maybe 20–40 lines of glue over writing the trait yourself against Laravel's whereFullText or Scout. Given zero dependents and a single 2024 release, the maintenance risk likely exceeds the saved code. Use it if you're already standardised on webard/eloquent-searchable across the app; otherwise implement the same trait locally and keep Nova's $search as documentation of intent.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect