Skip to main content

Filters

Filters are applied from the HTTP request's filter parameter. Only filters that are explicitly allowed are accepted — any unknown filter names throw an InvalidFilterQuery exception.

Exact

Applies a Scout where() with an = operator.

ScoutBuilder::for(Post::class, $request)
->allowedFilters(AllowedFilter::exact('status'));

Request: ?filter[status]=published

In

Applies a Scout whereIn() using a comma-separated list.

ScoutBuilder::for(Post::class, $request)
->allowedFilters(AllowedFilter::in('tags'));

Request: ?filter[tags]=php,laravel

Not In

Applies a Scout whereNotIn().

ScoutBuilder::for(Post::class, $request)
->allowedFilters(AllowedFilter::notIn('tags'));

Request: ?filter[tags]=spam,draft

Trashed

Includes soft-deleted records. Accepts with, only, or any other value to restore default behaviour.

ScoutBuilder::for(Post::class, $request)
->allowedFilters(AllowedFilter::trashed());

Request: ?filter[trashed]=only

Fixed Operator

Applies a where() with a fixed comparison operator.

use Foxws\ScoutBuilder\Enums\FilterOperator;

ScoutBuilder::for(Post::class, $request)
->allowedFilters(
AllowedFilter::operator('rating', FilterOperator::GreaterThan),
);

Request: ?filter[rating]=4

Available FilterOperator cases: Equal, NotEqual, LessThan, LessThanOrEqual, GreaterThan, GreaterThanOrEqual.

Dynamic Operator

Parses the operator from the filter value at runtime. Supports three input forms:

Colon-token string:

?filter[price]=gte:120

Array payload:

?filter[price][operator]=gte&filter[price][value]=120

Plain scalar (falls back to =):

?filter[price]=120

Available tokens: eq, neq / ne, lt, lte, gt, gte.

ScoutBuilder::for(Post::class, $request)
->allowedFilters(AllowedFilter::dynamicOperator('price'));

An invalid token (e.g. between:10,20) throws InvalidFilterValue.

Scope

Applies a named Eloquent scope via Scout's query() callback. Useful for database and collection drivers. Multiple scopes are chained without overwriting each other.

ScoutBuilder::for(Post::class, $request)
->allowedFilters(
AllowedFilter::scope('published'),
AllowedFilter::scope('of_category'),
);

Request: ?filter[published]=1&filter[of_category]=news

The filter name is converted to camelCase (of_categoryscopeOfCategory).

Note: The scope callback is silently ignored by remote engines (Algolia, Typesense, Meilisearch) since they do not execute Eloquent queries. Use this filter only with the database or collection driver, or add engine-awareness enforcement (see engine-awareness.md).

Callback

Apply custom filter logic with a closure.

ScoutBuilder::for(Post::class, $request)
->allowedFilters(
AllowedFilter::callback('search', function (Builder $query, mixed $value): void {
$query->where('title', $value)->orWhere('body', $value);
}),
);

Custom Filter Class

Implement the Filter interface for reusable filter logic.

use Foxws\ScoutBuilder\Filters\Filter;
use Laravel\Scout\Builder;

class FiltersPopular implements Filter
{
public function __invoke(Builder $query, mixed $value, string $property): void
{
$query->where('views', '>', (int) $value);
}
}

ScoutBuilder::for(Post::class, $request)
->allowedFilters(AllowedFilter::custom('popular', new FiltersPopular));

Modifiers

These modifiers can be chained on any AllowedFilter:

AllowedFilter::exact('status')
->default('published') // applied when the filter is absent from the request
->nullable() // allow null to pass through (skipped by default)
->ignore('draft', 'spam') // silently skip these values
->delimiter('|') // override the multi-value delimiter (default: ,)