Open source
Query Builder for Laravel
composer require roundly-consulting/query-builder-for-laravelOverview
A native query builder for Laravel API list endpoints. Declare which filters and sorts an endpoint allows, and it reads the query string — filter[…], sort, page, per_page — applying only those and rejecting everything else with an HTTP 400. The result is still an Eloquent builder, so you keep chaining with(), get() or paginate(). MIT-licensed, with no runtime dependencies beyond Laravel, Symfony and two small companion Roundly packages.
What you get
Allow-list by default
Only declared filters and sorts run; anything else is an HTTP 400, or silently dropped in ignore mode.
Rich built-in filters
Exact, boolean, partial, prefix/suffix, comparison, scope, callback, trashed, JSON-array membership and custom filters.
Client-chosen operators
not:, contains: or gte: from a set you declare; an undeclared token stays a literal value.
Nullable & relation filters
A none sentinel, not:none, whereDoesntHave negations and value shapes that prevent PostgreSQL 500s.
Validated pagination
HasPageSize validates per_page (422) and hard-caps it; the configured page name is applied automatically.
Hardened by design
Bound values, escaped LIKE wildcards, request limits and length-capped error messages.
Documentation
Installation
Install via Composer, optionally publish the config and translations, and inspect the live wire contract with artisan about.
Configuration
Every config key and its default — parameter names, page-size bounds, unknown-parameter modes and request limits.
Building queries
Wrap a model or a prepared builder with QueryBuilder::for(), declare the allow-list, and keep chaining like any Eloquent builder.
Filters
Every built-in filter constructor — exact, boolean, partial, prefix/suffix, scope, callback, trashed and custom — plus value normalisation.
Operator filters
Fixed server-side comparisons with operator(), and client-chosen operators such as not:, contains: and gte: with operators().
Nullable, relation & JSON filters
Filter nullable columns with a none sentinel, to-many relations with whereDoesntHave negations, and JSON array columns by membership.
Value shapes
Declare a FilterValueShape on typed columns so a malformed uuid or id returns an empty result instead of a PostgreSQL 500.
Validating filter values
Wrap your validation rules in FilterValue so operator prefixes and sentinels are stripped before the rules run.
Sorting
Allow-list sort fields, map public names to columns, sort by several columns and set a default order.
Pagination
Validate and hard-cap per_page with the HasPageSize FormRequest trait; the configured page name is applied automatically.
Wire contract
The frozen query-string syntax for filters, operators, sorts and pagination, and the status code each mistake returns.
Unknown parameters & errors
Un-allow-listed filters and sorts return HTTP 400 with a translatable, length-capped message — or are dropped in ignore mode.
Custom filters & sorts
Implement the Filter or Sort contract for bespoke logic, and parse the operator wire in callbacks with RequestedOperator::split().
Security model
How the package keeps request input away from SQL: allow-lists, bound values, escaped wildcards, request limits and capped error output.
Testing
Drive the builder with Request::create() in unit tests, and assert 400 and 422 responses on your endpoints.
Requirements
PHP 8.4+, Laravel 12 or 13, and two companion packages installed automatically — no migrations or extra PHP extensions.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.