Filters
Filter types, operators, and advanced filters
Filters let users narrow table results. You define them in PHP; the frontend renders a filter form from your definitions and sends the chosen values to query-table.
How filtering works (end to end)
- You return filter definitions from
filters()on your table class load-tableincludes them in thefiltersarray- User fills the filter form in the UI
- Frontend POSTs
filters: [{ field, operator, value }]toquery-table TableAbstract::prepareQueryBuilder()applies each clause to the Eloquent builder
Junior takeaway
The filter_name you set in PHP must match the field the frontend sends. If users filter by name but nothing changes, check this match first.
Filter classes
| Type key | Class | Typical use | Operators |
|---|---|---|---|
text | TextFilter | Name, email, search | =, like |
number | NumberFilter | Amount, quantity | =, !=, >, <, >=, <=, between |
select | SelectFilter | Status, category | = |
multi_select | MultiSelectFilter | Multiple statuses | in |
boolean | BooleanFilter | Yes/no flags | = |
null | NullFilter | Is null / not null | = |
date | DateFilter | Created at, due date | =, comparisons, between |
range | RangeFilter | Numeric/date ranges | between |
Examples
Text search
use Storageitsolutions\ApisTables\Filters\TextFilter;
public function filters(bool $api_array = true): array
{
return [
new TextFilter('name', 'name'), // label, filter_name
];
}Client sends:
{ "field": "name", "operator": "like", "value": "acme" }Select dropdown
use Storageitsolutions\ApisTables\Filters\SelectFilter;
new SelectFilter('status', 'status', [
'pending' => 'Pending',
'active' => 'Active',
'archived' => 'Archived',
]),Third argument is value => label options shown in the UI.
Date range
use Storageitsolutions\ApisTables\Filters\DateFilter;
new DateFilter('created_at', 'created_at'),Supports between for from/to date pickers.
Advanced filters
advancedFilters in structure JSON comes from filters(true) — same method, second call with $api_array = true. In most table classes both return the same array; override if you want a subset of filters only in the advanced panel.
Dynamic options (bindings)
Static dropdowns work for fixed enums. For DB-driven options:
protected function bindings(): array
{
return [
'category_id' => Category::pluck('name', 'id')
->map(fn ($name, $id) => ['value' => $id, 'label' => $name])
->values()
->toArray(),
];
}Returned in query-table response under bindings.category_id so the frontend can populate selects without a separate API.
Filtering on relations
If filter_name targets a relation column, ensure the join exists in setInitialBuilder():
$this->builder = Order::query()->join('customers', ...);Or use whereHas inside a custom filter callback (senior pattern via FiltersCallback trait).
Debugging checklist
| Issue | Fix |
|---|---|
| Filter has no effect | field ≠ filter_name |
| Wrong operator | Check allowed operators for filter type |
| Slow queries | Add DB index on filtered columns |
| Empty dropdown | Implement bindings() or pass select_options in SelectFilter |