# 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