# Filters
API Platform Core provides a generic system to apply filters on collections. Useful filters for the Doctrine ORM and
MongoDB ODM are provided with the library. You can also create custom filters that fit your specific needs.
You can also add filtering support to your custom [data providers](data-providers.md) by implementing interfaces provided
by the library.
By default, all filters are disabled. They must be enabled explicitly.
When a filter is enabled, it is automatically documented as a `hydra:search` property in the collection response. It also
automatically appears in the [NelmioApiDoc documentation](nelmio-api-doc.md) if it is available.
## Doctrine ORM and MongoDB ODM Filters
### Basic Knowledge
Filters are services (see the section on [custom filters](#creating-custom-filters)), and they can be linked
to a Resource in two ways:
1. Through the `ApiResource` declaration, as the `filters` attribute.
For example having a filter service declaration:
```yaml
# api/config/services.yaml
services:
# ...
offer.date_filter:
parent: 'api_platform.doctrine.orm.date_filter'
arguments: [ { dateProperty: ~ } ]
tags: [ 'api_platform.filter' ]
# The following are mandatory only if a _defaults section is defined
# You may want to isolate filters in a dedicated file to avoid adding them
autowire: false
autoconfigure: false
public: false
```
We're linking the filter `offer.date_filter` with the `@ApiResource` annotation:
```php
offer.date_filter
```
2. By using the `@ApiFilter` annotation.
This annotation automatically declares the service, and you just have to use the filter class you want:
```php
]=value`
The value can take any date format supported by the [`\DateTime` constructor](http://php.net/manual/en/datetime.construct.php).
The `after` and `before` filters will filter including the value whereas `strictly_after` and `strictly_before` will filter excluding the value.
Like others filters, the date filter must be explicitly enabled:
```php
`
Enable the filter:
```php
`
Enable the filter:
```php
]=value`
Enable the filter:
```php
`
Enable the filter:
```php
`
Enable the filter:
```php
`
Enable the filter:
```php
`
You can add as many groups as you need.
Enable the filter:
```php
&properties[][]=`
You can add as many properties as you need.
Enable the filter:
```php
isPropertyEnabled($property, $resourceClass) ||
!$this->isPropertyMapped($property, $resourceClass)
) {
return;
}
$parameterName = $queryNameGenerator->generateParameterName($property); // Generate a unique parameter name to avoid collisions with other filters
$queryBuilder
->andWhere(sprintf('REGEXP(o.%s, :%s) = 1', $property, $parameterName))
->setParameter($parameterName, $value);
}
// This function is only used to hook in documentation generators (supported by Swagger and Hydra)
public function getDescription(string $resourceClass): array
{
if (!$this->properties) {
return [];
}
$description = [];
foreach ($this->properties as $property => $strategy) {
$description["regexp_$property"] = [
'property' => $property,
'type' => 'string',
'required' => false,
'swagger' => [
'description' => 'Filter using a regex. This will appear in the Swagger documentation!',
'name' => 'Custom name to use in the Swagger documentation',
'type' => 'Will appear below the name in the Swagger documentation',
],
];
}
return $description;
}
}
```
Then, register this filter as a service:
```yaml
# api/config/services.yaml
services:
# ...
'App\Filter\RegexpFilter':
# Uncomment only if autoconfiguration isn't enabled
#tags: [ 'api_platform.filter' ]
```
In the previous example, the filter can be applied on any property. However, thanks to the `AbstractFilter` class,
it can also be enabled for some properties:
```yaml
# api/config/services.yaml
services:
'App\Filter\RegexpFilter':
arguments: [ '@doctrine', ~, '@?logger', { email: ~, anOtherProperty: ~ } ]
# Uncomment only if autoconfiguration isn't enabled
#tags: [ 'api_platform.filter' ]
```
Finally, add this filter to resources you want to be filtered:
```php
$context['filters']['fullName'],
'operator' => 'and',
];
$requestBody['query']['constant_score']['filter']['bool']['must'][0]['match']['full_name'] = $andQuery;
return $requestBody;
}
}
```
### Using Doctrine ORM Filters
Doctrine ORM features [a filter system](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/filters.html) that allows the developer to add SQL to the conditional clauses of queries, regardless of the place where the SQL is generated (e.g. from a DQL query, or by loading associated entities).
These are applied on collections and items and therefore are incredibly useful.
The following information, specific to Doctrine filters in Symfony, is based upon [a great article posted on Michaƫl Perrin's blog](http://blog.michaelperrin.fr/2014/12/05/doctrine-filters/).
Suppose we have a `User` entity and an `Order` entity related to the `User` one. A user should only see his orders and no one else's.
```php
reader) {
throw new \RuntimeException(sprintf('An annotation reader must be provided. Be sure to call "%s::setAnnotationReader()".', __CLASS__));
}
// The Doctrine filter is called for any query on any entity
// Check if the current entity is "user aware" (marked with an annotation)
$userAware = $this->reader->getClassAnnotation($targetEntity->getReflectionClass(), UserAware::class);
if (!$userAware) {
return '';
}
$fieldName = $userAware->userFieldName;
try {
// Don't worry, getParameter automatically escapes parameters
$userId = $this->getParameter('id');
} catch (\InvalidArgumentException $e) {
// No user id has been defined
return '';
}
if (empty($fieldName) || empty($userId)) {
return '';
}
return sprintf('%s.%s = %s', $targetTableAlias, $fieldName, $userId);
}
public function setAnnotationReader(Reader $reader): void
{
$this->reader = $reader;
}
}
```
Now, we must configure the Doctrine filter.
```yaml
# api/config/packages/api_platform.yaml
doctrine:
orm:
filters:
user_filter:
class: App\Filter\UserFilter
```
Add a listener for every request that initializes the Doctrine filter with the current user in your bundle services declaration file.
```yaml
# api/config/services.yaml
services:
# ...
'App\EventListener\UserFilterConfigurator':
tags:
- { name: kernel.event_listener, event: kernel.request, priority: 5 }
# Autoconfiguration must be disabled to set a custom priority
autoconfigure: false
```
It's key to set the priority higher than the `ApiPlatform\Core\EventListener\ReadListener`'s priority, as flagged in [this issue](https://github.com/api-platform/core/issues/1185), as otherwise the `PaginatorExtension` will ignore the Doctrine filter and return incorrect `totalItems` and `page` (first/last/next) data.
Lastly, implement the configurator class:
```php
em = $em;
$this->tokenStorage = $tokenStorage;
$this->reader = $reader;
}
public function onKernelRequest(): void
{
if (!$user = $this->getUser()) {
throw new \RuntimeException('There is no authenticated user.');
}
$filter = $this->em->getFilters()->enable('user_filter');
$filter->setParameter('id', $user->getId());
$filter->setAnnotationReader($this->reader);
}
private function getUser(): ?UserInterface
{
if (!$token = $this->tokenStorage->getToken()) {
return null;
}
$user = $token->getUser();
return $user instanceof UserInterface ? $user : null;
}
}
```
Done: Doctrine will automatically filter all "UserAware" entities!
## ApiFilter Annotation
The annotation can be used on a `property` or on a `class`.
If the annotation is given over a property, the filter will be configured on the property. For example, let's add a search filter on `name` and on the `prop` property of the `colors` relation:
```php