Skip to content

Filters ​

Filters add controls to the listing's filter bar, above the free-text search. They are declared inside filters():

php
public function filters(): array
{
    return [
        TernaryFilter::make('is_published')
            ->label('Status')
            ->trueLabel('Published')
            ->falseLabel('Draft'),

        SelectFilter::make('category')
            ->options(['news' => 'News', 'update' => 'Update', 'announcement' => 'Announcement']),

        DateRangeFilter::make('published_at')
            ->label('Published between'),
    ];
}

The Filter base class ​

Every filter implements type() (how the control is rendered on the frontend) and apply(Builder $query, mixed $value): Builder (how the chosen value is translated into a query clause). The label(string $label) method is common to all of them.

To override the matching logic of a specific filter without creating a new subclass:

php
SelectFilter::make('category')
    ->options([...])
    ->query(fn (Builder $query, mixed $value): Builder => $query->where('category', $value)->whereNotNull('published_at'))

query() replaces apply() entirely when provided.

Built-in filters ​

SelectFilter ​

Filters by exact equality over a dropdown of options.

php
SelectFilter::make('category')
    ->options(['news' => 'News', 'update' => 'Update', 'announcement' => 'Announcement'])

Internally: $query->where($this->name, $value).

TernaryFilter ​

Three states: yes / no / any. Meant for boolean columns.

php
TernaryFilter::make('is_published')
    ->trueLabel('Published')   // default: "Yes"
    ->falseLabel('Draft')      // default: "No"
    ->placeholder('Any status') // default: "Any"

It accepts '1'/'true'/true as "yes" and '0'/'false'/false as "no"; any other value filters nothing.

DateRangeFilter ​

A from/to range over a date column.

php
DateRangeFilter::make('published_at')
    ->label('Published between')
    ->displayFormat('d/m/Y')

The expected value is an array ['from' => ..., 'to' => ...]; either key is optional, and each one is translated into an independent whereDate('>=', …) or whereDate('<=', …).

RelationFilter ​

Filters the listing by a related record, chosen from a searchable combobox, the same component as the Relation field, but as a filter.

php
RelationFilter::make('author_id')
    ->related(User::class)
    ->titleAttribute('name')
    ->optionsQuery(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at'))
MethodDescription
related(class-string<Model> $model)The related model.
titleAttribute(string $attribute)Attribute shown in each option. name by default.
searchColumns(string ...$columns)Columns the combobox searches against.
optionsQuery(Closure $callback)Restricts which records appear in the picker.
placeholder(string $text)Text when nothing is selected. Any by default.

optionsQuery() and not query() on purpose: the base Filter already has an inherited query(Closure) that overrides how the filter matches the records (see "The Filter base class" above); reusing that name for "which options are offered" would silently collide with it. RelationFilter serves its own options endpoint, separate from that of a Relation field, precisely because a filter and a field can share the same name (filtering by the same column that a form field already picks).

This first version only supports a single value, there is no ->multiple() variant yet.