foxws/laravel-scout-builder
A fluent query builder for Laravel Scout that lets you construct search queries with familiar chainable methods, making it easier to filter, sort, and paginate searchable Eloquent models while keeping Scout integrations clean and expressive.
foxws/laravel-scout-builder sits on top of Laravel Scout and gives you a fluent, object-oriented way to build the driver-specific filter expressions (Meilisearch, Typesense, Algolia) that you would otherwise hand-write as strings inside ->where(...). This assessment reflects v1.2.0.
composer require foxws/laravel-scout-builderphp artisan vendor:publish --tag="scout-builder-config"config/scout.php) and your model uses Laravel\Scout\Searchable.The first use case to reach for: any endpoint where filter input is user-driven (?price_min=…&status=…&rating>=4). Instead of concatenating filter strings and hoping the syntax is valid, you declare filters and let the package compile them. If you're currently doing Model::search($q)->where('price > 100 AND status = "active"'), that's the code to migrate first — it is exactly where malformed input, unescaped quotes, and injection-shaped bugs come from.
Filter-per-concern classes. Define a filter class per model or per index (status, price range, date range, owner). This keeps the builder declarative and testable, and lets the package normalise nullability and operator tokens in one place.
Pass everything, let nulls no-op. The dominant pattern after 1.2.0 is to always hand the builder the full set of filters, including ones the user didn't supply. A nullable filter whose value is null is now fully ignored, so it drops out of the compiled expression instead of producing an empty or = "" constraint. This removes a whole layer of if ($request->filled(...)) branching from your controllers.
Dynamic operator tokens. Filters that accept tokens embedded in the value (e.g. >=500, !=draft) are resolved by the package. As of 1.2.0, an unrecognised operator token no longer breaks or silently miscompiles the query — it degrades to equality. Treat the token map as a whitelist you control, not as free-form input validation.
Composition. The typical call site reads as: build for a model → attach the filter set → attach the search term → get()/paginate(). Keep the builder in a form request or a dedicated query object so the compiled expression is reproducible in tests.
Testing. Assert on the compiled filter string (or the builder's toArray()/__toString() equivalent) rather than hitting the search engine. This is fast, deterministic, and is the only practical way to catch regressions in operator resolution — including the 1.2.0 fallback behaviour.
Reuse across drivers. Because the builder owns the quoting/escaping, the same filter definitions can target Meilisearch in one environment and Typesense in another. Centralise escaping there and never interpolate raw user input into a where() call alongside it.
null anywhere means the filter disappears.null is skipped; "" and 0 are values and can still generate constraints (status = ""). Normalise '' to null in your request layer if the UI sends empty fields.=>500 (typo for >=500) now compiles to equality against the literal string. When a filter "returns no results", dump the compiled expression before suspecting the engine.How can I help you explore Laravel packages today?