-
Notifications
You must be signed in to change notification settings - Fork 2
QueryFilter
Viames Marino edited this page Feb 23, 2026
·
2 revisions
Pair\Api\QueryFilter applies REST-style filtering/sorting/pagination to ActiveRecord list endpoints.
It is used by CrudController to parse request parameters and transform them into Pair\Orm\Query constraints.
filter[field]=valuefilter[field]=!value-
filter[field]=>=N,<=N,>N,<N -
filter[field]=v1,v2,v3(IN) -
filter[field]=null,!null sort=field,-otherFieldsearch=keywordfields=field1,field2include=rel1,rel2page=NperPage=N
Allowed filters/sorts/search fields come from model API config (ApiExposable pattern), for example:
filterablesortablesearchableincludesperPagemaxPerPagedefaultSort
Returns:
-
query(Pair\Orm\Query) pageperPagefieldsincludes
Builds count query with current filters/search (without sort/pagination) to compute pagination metadata.
GET /api/faqs?filter[status]=published&search=payment&sort=-created&page=2&perPage=20
Effects:
- where status = published
- full-text-like search on configured searchable fields
- order by created DESC
- page 2, size 20
- Disallowed or unknown fields are ignored.
-
perPageis clamped to configured maximum. - Sparse fieldsets (
fields) and includes (include) are parsed but actual response shaping depends onResource/transform logic.
GET /api/users?filter[active]=1&filter[groupId]=2,3&search=ada&sort=-createdAt,email&page=2&perPage=30&fields=id,email,active&include=group
Typical effects:
active = 1-
group_id IN (2,3)(through model binds) - search across configured searchable fields
- order by
created_at DESC, thenemail ASC - page 2, size 30 (bounded by
maxPerPage) - sparse output fields and allowed includes parsed
public static function getApiConfig(): array
{
return [
'filterable' => ['id', 'email', 'active', 'groupId'],
'sortable' => ['id', 'email', 'createdAt'],
'searchable' => ['email', 'fullName'],
'includes' => ['group'],
'defaultSort' => '-id',
'perPage' => 20,
'maxPerPage' => 100,
];
}$request = new \Pair\Api\Request();
$filter = new \Pair\Api\QueryFilter(\App\Orm\User::class, $request, \App\Orm\User::getApiConfig());
$total = $filter->count();
$result = $filter->apply();
$query = $result['query'];
$rows = \Pair\Orm\Database::load($query->toSql(), $query->getBindings());- Expecting filters on fields not listed in
filterable. - Assuming
fieldsautomatically enforces DB-level select (it is response shaping). - Forgetting bind names: filters use model property names, not raw DB column names.
- Relying on
sortinput without whitelisting insortable.
See also: API, Request, ApiResponse, ActiveRecord, Resource.