Skip to content

Own filters

A filter of your own extends Anvil\Collection\Filters\Filter: it declares its inputs and their rules, turns them into a value, applies the value to the query and renders the control.

Terminal window
php spark make:anvil-filter MinimumAmount
namespace App\Filters;
use Anvil\Collection\Filters\Filter;
use Anvil\Data\Model;
final class MinimumAmount extends Filter
{
public function rules(): array
{
return [$this->inputName() => ['permit_empty', 'is_natural']];
}
public function value(array $input): ?string
{
$value = self::input($input, $this->inputName());
return $value === '' ? null : $value;
}
public function apply(Model $model, array|string $value): void
{
$model->where($this->qualifiedColumn($model) . ' >=', $value);
}
protected function view(): string
{
return 'filters/minimum_amount';
}
}
app/Views/filters/minimum_amount.php
<input type="number" min="0" class="form-control w-auto" name="<?= esc($filter->inputName(), 'attr') ?>"
value="<?= esc((string) ($value ?? ''), 'attr') ?>" aria-label="<?= esc($filter->getLabel(), 'attr') ?>"
placeholder="<?= esc($filter->getLabel(), 'attr') ?>"
hx-post="<?= esc($endpoint, 'attr') ?>" hx-sync="closest form:replace" <?= $swap ?> hx-trigger="change">
Method Purpose
rules() input name => validation rules. Only these inputs reach the filter; invalid input is rejected with 422
value(array $input) the active value from the validated input (a string or an array of strings), null when the filter is not set
apply(Model $model, $value) adds the conditions to the query
view() the view of the control: a full view name such as filters/minimum_amount
toInput($value) inverse of value(): the inputs that lead to a value; needed for filters with several inputs or formatted values, so saved views can be applied

Helpers of Filter: inputName('suffix') (filter_<name>_<suffix>), qualifiedColumn($model) (table.column), self::input($input, $name) (a trimmed string).

It receives $filter, $value (the active value or null), $endpoint and $swap. Every input must carry

hx-post="<?= esc($endpoint, 'attr') ?>" hx-sync="closest form:replace" <?= $swap ?> hx-trigger="change"

so a change reloads the list. Escape every value; no inline styles or scripts.

  • Use the query builder in apply() with the validated value; never build SQL from input.
  • Keep value() strict: return null for anything that is not a valid value.
  • A value that is an array (ranges) must have string values; it is stored in saved views as it is.

More: Extending Anvil.