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 ...
];
Logical AND / OR
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.
NumericFiltermatches value against numeric columnStringFiltermatches value against string columnBooleanFiltermarches value against boolean columnDateFiltermatches value against date column (Y-m-d)DatetimeFiltermatches value against datetime column (Y-m-d H:i:s)UTCDatetimeFiltermatches value against datetime column (Y-m-d H:i:s). Given date is converted to UTC, before matched against database valueBelongsToFilterable to constrain relations of the type "belongs to" (see further below for example)
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',
]);
];