Filters
Filters narrow the list with controls in the toolbar. All filters are in
Anvil\Collection\Filters and are created with ::make('name'):
use Anvil\Collection\Filters\DateRangeFilter;use Anvil\Collection\Filters\NumberRangeFilter;use Anvil\Collection\Filters\SelectFilter;use Anvil\Collection\Filters\TernaryFilter;use Anvil\Collection\Filters\TextFilter;use Anvil\Collection\Filters\ToggleFilter;
->filters([ SelectFilter::make('stage')->options(DealResource::STAGES)->multiple(), TernaryFilter::make('newsletter'), TernaryFilter::make('ended')->column('ended_at')->nullable()->trueLabel('Ended')->falseLabel('Active'), DateRangeFilter::make('expected_close_on')->label('Expected close'), NumberRangeFilter::make('amount'), TextFilter::make('city'), ToggleFilter::make('mine')->label('My requests') ->query(static fn (Model $requests) => $requests->where('budget_owner_id', auth()->id())),])| Filter | Control | Query |
|---|---|---|
| SelectFilter | a select, or checkboxes in a dropdown | = value, IN (…) |
| TernaryFilter | all / yes / no | = 1, = 0 OR NULL; or IS NOT NULL / IS NULL |
| DateRangeFilter | from, until | >= from, < until + 1 day |
| NumberRangeFilter | min, max in the locale format | >= min, <= max |
| TextFilter | text | contains, * as wildcard; or equal |
| ToggleFilter | a switch button | an own query |
| Own filters | your view | your query |
Options of every filter
Section titled “Options of every filter”| Option | Effect |
|---|---|
label('…') |
Label of the control (default: the humanized name) |
column('…') |
The column to filter when it differs from the filter name, e.g. SelectFilter::make('customer')->column('customer_id') |
The name must be a plain name ([a-z_][a-z0-9_]*, at most 64 characters),
the column as well; otherwise an InvalidArgumentException. Filters work
on columns of the main table (table.column); the
TextFilter can filter joined columns and
expressions too, a ToggleFilter any
query.
How filters work
Section titled “How filters work”- Every input of a filter has validation rules. Invalid values (an option that does not exist, a date that is not a date) are rejected with 422; only validated values reach the query.
- A change of a control reloads the list and starts on page 1.
- Active filters are combined with AND, and with the search. The toolbar shows how many filters are active and offers Reset filters.
- Filters run after the tenant scope and
scopeQuery()of the resource: they can only narrow what the user may see. - The controls sit in the toolbar next to the search, in the order of
filters(), and wrap on narrow screens.
Saved views and presets
Section titled “Saved views and presets”The values of the filters are part of saved views and presets. In presets they are written as:
| Filter | Value in Preset::filters() |
|---|---|
SelectFilter |
'open', several 'open,won' |
TernaryFilter |
'1' or '0' |
ToggleFilter |
'1' |
DateRangeFilter |
['from' => '2026-01-01', 'until' => '2026-12-31'] |
NumberRangeFilter |
['min' => '1000', 'max' => '5000.50'] (point as decimal separator) |
TextFilter |
'Berlin', 'mei*' |
A saved view or preset whose filter no longer exists, or whose value is no longer valid, falls back to the default for that filter.
With the API enabled, the same filters are query parameters:
GET /api/v1/deals?filter[stage]=open,won&filter[amount][min]=1000&filter[expected_close_on][from]=2026-01-01filter[<name>] for one value (several separated by commas for
multiple()), filter[<name>][from|until] for dates and
filter[<name>][min|max] for numbers with a point. Unknown filters are
rejected with 422.