Filtering
allowedFilters() lists the filters a request may use. Filters are passed as filter[<name>]=<value>:
QueryBuilder::for(User::class)
->allowedFilters('name', AllowedFilter::exact('id'))
->get();GET /users?filter[name]=john&filter[id]=1,2,3A filter not in the list throws InvalidFilterQuery.
Values
- A value with the delimiter is a list:
filter[id]=1,2,3is[1, 2, 3]. trueandfalsebecome booleans.- An empty value (
filter[name]=) is ignored, unless the filter is nullable.
Filter types
Partial
A string, or AllowedFilter::partial('name'). Matches when the column contains the value, ignoring case: LOWER(name) LIKE '%john%'. A list matches any of its values.
AllowedFilter::beginsWith('name') and AllowedFilter::endsWith('name') match the start or the end of the column.
Exact
AllowedFilter::exact('id') compares with =, and a list with in. Useful for ids, statuses and booleans:
GET /users?filter[id]=1,2&filter[admin]=trueOperator
AllowedFilter::operator('salary', FilterOperator::GREATER_THAN) compares with a fixed operator. With FilterOperator::DYNAMIC the client puts the operator at the start of the value:
use MyProgs\QueryBuilder\Enums\FilterOperator;
AllowedFilter::operator('salary', FilterOperator::DYNAMIC)GET /users?filter[salary]=>=3000The operators are =, <>, <, >, <= and >=. A value without one compares with =. The third argument, 'or', joins the comparison with or instead of and.
Scope
AllowedFilter::scope('active') calls a local scope of the model with the value as its argument; a list is passed as several arguments:
public function scopeCreatedBetween(Builder $query, $from, $to): void
{
$query->whereBetween('created_at', [$from, $to]);
}AllowedFilter::scope('created_between')GET /users?filter[created_between]=2026-01-01,2026-01-31The filter name is converted to camel case to find the scope. A scope parameter typed as a model receives the model resolved by route binding, and an unknown key throws InvalidFilterValue.
Belongs to
AllowedFilter::belongsTo('author') filters by the key of a belongsTo relation with whereBelongsTo(): filter[author]=1,2. A dotted name filters through relations: AllowedFilter::belongsTo('post.author').
Trashed
AllowedFilter::trashed() for models with SoftDeletes: filter[trashed]=with includes deleted models, filter[trashed]=only returns only them, any other value leaves them out.
Callback
AllowedFilter::callback('has_posts', function (Builder $query, mixed $value, string $property) {
$query->whereHas('posts');
})Custom filter classes
Implement MyProgs\QueryBuilder\Filters\Filter and register it with custom():
use Illuminate\Database\Eloquent\Builder;
use MyProgs\QueryBuilder\Filters\Filter;
class FiltersUserPermission implements Filter
{
public function __invoke(Builder $query, mixed $value, string $property): void
{
$query->whereHas('permissions', fn (Builder $query) => $query->where('name', $value));
}
}AllowedFilter::custom('permission', new FiltersUserPermission)Groups
AllowedFilter::groupOr() applies one value to several filters joined with or, inside one parenthesised where; groupAnd() joins them with and:
AllowedFilter::groupOr('search', [
AllowedFilter::partial('name'),
AllowedFilter::partial('email'),
])GET /users?filter[search]=johnFiltering through relations
A dotted name filters through a relation with whereHas():
AllowedFilter::exact('posts.status')GET /users?filter[posts.status]=publishedPass false as the third argument of exact, partial, beginsWith, endsWith or operator to treat the dot as part of a column name instead (AllowedFilter::exact('meta.type', null, false)).
Options
Another column name
The second argument is the column, or the scope, behind the name the client uses:
AllowedFilter::exact('author', 'user_id')Default values
AllowedFilter::exact('status')->default('published')The default is used when the request does not mention the filter. unsetDefault() removes it.
Nullable filters
AllowedFilter::exact('deleted_at')->nullable()An empty value (filter[deleted_at]=) then matches NULL instead of being ignored. A null default makes the filter nullable too.
Ignored values
AllowedFilter::exact('status')->ignore('all', null)The listed values are dropped from the request value; a filter left with nothing is not applied.
Delimiter
AllowedFilter::exact('name')->delimiter('|')The delimiter of this filter only: filter[name]=Smith, John|Doe, Jane. The default comes from the delimiter config key.