Skip to content
laravel-nova-examples.com

Create a select filter with a default value

A typed Nova 5 select filter for an order status column, with a default selection and a reusable variant that takes the column name as a constructor argument.

Nova
5.x
Published
Reading time
2 min

Most admin lists need a quick way to narrow records by status. In Nova that is a select filter: a class with an apply() method that modifies the query and an options() method that returns the choices.

Generate the filter

php artisan nova:filter OrderStatus

The filter class

<?php

namespace App\Nova\Filters;

use Illuminate\Contracts\Database\Eloquent\Builder;
use Laravel\Nova\Filters\Filter;
use Laravel\Nova\Http\Requests\NovaRequest;

class OrderStatus extends Filter
{
    public $component = 'select-filter';

    public function apply(NovaRequest $request, Builder $query, mixed $value): Builder
    {
        return $query->where('status', $value);
    }

    public function options(NovaRequest $request): array
    {
        return [
            'Pending' => 'pending',
            'Paid' => 'paid',
            'Shipped' => 'shipped',
            'Cancelled' => 'cancelled',
        ];
    }

    public function default()
    {
        return 'paid';
    }
}

Note the direction of the options() array: the label is the key and the stored value is the array value. Swapping them is the most common reason a filter shows the wrong text or matches nothing.

default() preselects an option when the index loads, so the list opens on paid orders. Users can still pick a different status in the filter menu.

Register the filter on the resource

use Laravel\Nova\Http\Requests\NovaRequest;

public function filters(NovaRequest $request): array
{
    return [
        new Filters\OrderStatus,
    ];
}

A reusable filter for any status column

If several resources filter on a status column with different names, pass the column into the constructor instead of copying the class:

<?php

namespace App\Nova\Filters;

use Illuminate\Contracts\Database\Eloquent\Builder;
use Laravel\Nova\Filters\Filter;
use Laravel\Nova\Http\Requests\NovaRequest;

class StatusFilter extends Filter
{
    public $component = 'select-filter';

    public function __construct(
        protected string $column = 'status',
        protected array $statuses = [],
    ) {}

    public function apply(NovaRequest $request, Builder $query, mixed $value): Builder
    {
        return $query->where($this->column, $value);
    }

    public function options(NovaRequest $request): array
    {
        return $this->statuses;
    }

    public function key(): string
    {
        return 'status_filter_'.$this->column;
    }
}
public function filters(NovaRequest $request): array
{
    return [
        new Filters\StatusFilter('payment_status', [
            'Open' => 'open',
            'Paid' => 'paid',
        ]),
    ];
}

Overriding key() gives each instance its own key. Without it, two instances of the same filter class on one resource would share a key and overwrite each other's selection.

Reference