Constraints Processor
As the name implies, the ConstraintsProcessor
is able to create constraint query filters, e.g. "where column operator value
". The http query string format is slightly inspired by JSON API. The accepted format is:
?identifier[property][operator]=value
Example
?filter[name][contains]=Smith&filter[is_admin][eq]=true
The shown query string will create a query that matches records where name
contains Smith
and is_admin
state is set to true
.
Setup
When configuring the processor, you must specify the properties and their corresponding "field filter" that can be applied, when requested.
use Aedart\Filters\Processors\ConstraintsProcessor;
use Aedart\Filters\Query\Filters\Fields\NumericFilter;
use Aedart\Filters\Query\Filters\Fields\StringFilter;
use Aedart\Filters\Query\Filters\Fields\BooleanFilter;
use Aedart\Filters\Query\Filters\Fields\DatetimeFilter;
class UserFilterBuilder extends BaseBuilder
{
public function processors(): array
{
return [
'filter' => ConstraintsProcessor::make()
->filters([
'id' => NumericFilter::class,
'name' => StringFilter::class,
'email' => StringFilter::class,
'administrator' => BooleanFilter::class,
'email_verified' => DatetimeFilter::class,
'created_at' => DatetimeFilter::class,
'updated_at' => DatetimeFilter::class,
])
// ...etc
];
}
}
Properties to column names
Similar to how you can map properties to table column names on the SortingProcessor
, the "constraints" processor also offers a propertiesToColumns()
method, which can be used to map requested properties to table columns.
return [
'filter' => ConstraintsProcessor::make()
->filters([
'name' => StringFilter::class,
'email' => StringFilter::class,
'administrator' => BooleanFilter::class,
'email_verified' => DatetimeFilter::class,
])
->propertiesToColumns([
'administrator' => 'is_admin',
'email_verified' => 'email_verified_at'
]);
];
Maximum amount of allowed filters
By default, 10
properties are allowed requested to be filtered. When more are requested, then a validation exception will be thrown that results in a 422 Unprocessable Entity
response. To change this limit, use the maxFilters()
method.
return [
'filter' => ConstraintsProcessor::make()
->maxFilters(15)
// ...remaining processor config. not shown ...
];
AND
/ OR
Logical When multiple properties are requested, then constraint filters are applied using logical AND
operator. If you wish to allow OR
operator, then please see the MatchingProcessor
documentation for details.
Available Filters
Within the Aedart\Filters\Query\Filters\Fields\
namespace, you will find a few predefined "field filter". These can be applied per allowed filterable property.
NumericFilter
matches value against numeric columnStringFilter
matches value against string columnBooleanFilter
marches value against boolean columnDateFilter
matches value against date column (Y-m-d
) - Available sincev5.23.x
DatetimeFilter
matches value against datetime column (Y-m-d H:i:s
)UTCDatetimeFilter
matches value against datetime column (Y-m-d H:i:s
). Given date is converted to UTC, before matched against database value - Available sincev5.23.x
BelongsToFilter
able to constrain relations of the type "belongs to" (see further below for example) - Available sincev5.24.x
Operators
All available filters support a variety of operators, which can be requested in the http query string. These operators are either mapped directly to an SQL comparison operator or function, which is then applied in the query.
Example
eq
==
ne
=!=
gt
=>
lt
=<
is_null
=is null
,contains
=like %value%
If an unsupported operator is requested, then the request is aborted - a 422 Unprocessable Entity
response is returned. Please review each filter's source for a full list of supported operators.
Create your own filters
If the available filters are not sufficient, then you can create your own filters, which can be supported by the "constraints" processor. The easiest way of doing so, is by extending the BaseFieldFilter
abstraction.
Example:
use Aedart\Filters\Query\Filters\Fields\BaseFieldFilter;
class MyFilter extends BaseFieldFilter
{
public function apply($query)
{
$operator = $this->operator();
switch ($operator) {
case 'special':
return $query->where($this->field(), '%', $this->value());
default:
return $this->buildDefaultConstraint($query);
}
}
public function operatorAliases(): array
{
return [
'eq' => '=',
'ne' => '!=',
'special' => 'special'
];
}
protected function assertValue($value)
{
// The assert method can be used to validate the requested value.
// Throw an InvalidArgumentException, if value is invalid.
// The processor will take care of the rest...
if ((int) $value < 1) {
throw new InvalidArgumentException('Value must be above 1');
}
}
}
BelongsToFilter
Example
The BelongsToFilter
is slightly more special in that you need to create an instance and specify the name of the relation you wish to constrain.
use Aedart\Filters\Query\Filters\Fields\BelongsToFilter;
return [
'filter' => ConstraintsProcessor::make()
->filters([
'user' => BelongsToFilter::make()
->setRelation('owner'),
// ...remaining not shown...
])
->propertiesToColumns([
'user' => 'owner.id', // relation + name of column in related model
]);
];
String relation
By default, the belongs to filter assumes that the relation value to constrain is an integer. If you wish to constrain a string value instead, e.g. a string column, then use the usingStringValue()
method.
return [
'filter' => ConstraintsProcessor::make()
->filters([
'category' => BelongsToFilter::make()
->setRelation('categories')
->usingStringValue(),
// ...remaining not shown...
])
->propertiesToColumns([
'category' => 'categories.slug',
]);
];