+
+```php
+
+
+**Note: Filters on nested properties must still be enabled explicitly, in order to keep things sane.**
+
+Regardless of this option, filters can be applied on a property only if:
+
+- the property exists
+- the value is supported (ex: `asc` or `desc` for the order filters).
+
+It means that the filter will be **silently** ignored if the property:
+
+- does not exist
+- is not enabled
+- has an invalid value
+
+
+## Decorate a Doctrine filter using Symfony
+
+A filter that implements the `ApiPlatform\Doctrine\Common\Filter\PropertyAwareFilterInterface` interface can be decorated:
+
+```php
+namespace App\Doctrine\Filter;
+
+use ApiPlatform\Doctrine\Common\Filter\PropertyAwareFilterInterface;
+use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
+use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
+use ApiPlatform\Metadata\Operation;
+use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\DependencyInjection\Attribute\Autowire;
+
+final class SearchTextAndDateFilter implements FilterInterface
+{
+ public function __construct(#[Autowire('@api_platform.doctrine.orm.search_filter.instance')] readonly FilterInterface $searchFilter, #[Autowire('@api_platform.doctrine.orm.date_filter.instance')] readonly FilterInterface $dateFilter, protected ?array $properties = null, private array $dateFilterProperties = [], private array $searchFilterProperties = [])
+ {
+ }
+
+ // This function is only used to hook in documentation generators (supported by Swagger and Hydra)
+ public function getDescription(string $resourceClass): array
+ {
+ if ($this->searchFilter instanceof PropertyAwareFilterInterface) {
+ $this->searchFilter->setProperties($this->searchFilterProperties);
+ }
+ if ($this->dateFilter instanceof PropertyAwareFilterInterface) {
+ $this->dateFilter->setProperties($this->dateFilterProperties);
+ }
+
+ return array_merge($this->searchFilter->getDescription($resourceClass), $this->dateFilter->getDescription($resourceClass));
+ }
+
+ public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
+ {
+ if ($this->searchFilter instanceof PropertyAwareFilterInterface) {
+ $this->searchFilter->setProperties($this->searchFilterProperties);
+ }
+ if ($this->dateFilter instanceof PropertyAwareFilterInterface) {
+ $this->dateFilter->setProperties($this->dateFilterProperties);
+ }
+
+ $this->searchFilter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $context['filters']['searchOnTextAndDate']] + $context);
+ $this->dateFilter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $context['filters']['searchOnTextAndDate']] + $context);
+ }
+}
+```
+
+This can be used with parameters using attributes:
+
+```php
+namespace App\Entity;
+
+use ApiPlatform\Metadata\QueryParameter;
+
+#[GetCollection(
+ uriTemplate: 'search_filter_parameter{._format}',
+ parameters: [
+ 'searchOnTextAndDate[:property]' => new QueryParameter(filter: 'app_filter_date_and_search'),
+ ]
+)]
+// Note that we link the parameter filter and this filter using the "alias" option:
+#[ApiFilter(SearchTextAndDateFilter::class, alias: 'app_filter_date_and_search', properties: ['foo', 'createdAt'], arguments: ['dateFilterProperties' => ['createdAt' => 'exclude_null'], 'searchFilterProperties' => ['foo' => 'exact']])]
+#[ORM\Entity]
+class SearchFilterParameter
+{
+ /**
+ * @var int The id
+ */
+ #[ORM\Column(type: 'integer')]
+ #[ORM\Id]
+ #[ORM\GeneratedValue(strategy: 'AUTO')]
+ private ?int $id = null;
+ #[ORM\Column(type: 'string')]
+ private string $foo = '';
+
+ #[ORM\Column(type: 'datetime_immutable', nullable: true)]
+ private ?\DateTimeImmutable $createdAt = null;
+
+ public function getId(): ?int
+ {
+ return $this->id;
+ }
+
+ public function getFoo(): string
+ {
+ return $this->foo;
+ }
+
+ public function setFoo(string $foo): void
+ {
+ $this->foo = $foo;
+ }
+
+ public function getCreatedAt(): ?\DateTimeImmutable
+ {
+ return $this->createdAt;
+ }
+
+ public function setCreatedAt(\DateTimeImmutable $createdAt): void
+ {
+ $this->createdAt = $createdAt;
+ }
+}
+```
+
+## Using Doctrine ORM Filters
+
+Doctrine ORM features [a filter system](https://www.doctrine-project.org/projects/doctrine-orm/en/current/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 to 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](https://www.michaelperrin.fr/blog/2014/12/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
+getReflectionClass()->getAttributes(UserAware::class)[0] ?? null;
+
+ $fieldName = $userAware?->getArguments()['userFieldName'] ?? null;
+ if ($fieldName === '' || is_null($fieldName)) {
+ return '';
+ }
+
+ 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);
+ }
+}
+```
+
+Now, we must configure the Doctrine filter.
+
+```yaml
+# api/config/packages/api_platform.yaml
+doctrine:
+ orm:
+ filters:
+ user_filter:
+ class: App\Filter\UserFilter
+ enabled: true
+```
+
+Done: Doctrine will automatically filter all `UserAware`entities!
+
+## Creating Custom Doctrine ORM Filters
+
+Doctrine ORM filters have access to the context created from the HTTP request and to the `QueryBuilder` instance used to
+retrieve data from the database. They are only applied to collections. If you want to deal with the DQL query generated
+to retrieve items, [extensions](extensions.md) are the way to go.
+
+A Doctrine ORM filter is basically a class implementing the `ApiPlatform\Doctrine\Orm\Filter\FilterInterface`.
+API Platform includes a convenient abstract class implementing this interface and providing utility methods: `ApiPlatform\Doctrine\Orm\Filter\AbstractFilter`.
+
+In the following example, we create a class to filter a collection by applying a regular expression to a property.
+The `REGEXP` DQL function used in this example can be found in the [`DoctrineExtensions`](https://github.com/beberlei/DoctrineExtensions)
+library. This library must be properly installed and registered to use this example (works only with MySQL).
+
+```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' => Type::BUILTIN_TYPE_STRING,
+ 'required' => false,
+ 'description' => 'Filter using a regex. This will appear in the OpenApi documentation!',
+ 'openapi' => new Parameter(
+ name: $property,
+ in: 'query',
+ allowEmptyValue: true,
+ explode: false, // to be true, the type must be Type::BUILTIN_TYPE_ARRAY, ?product=blue,green will be ?product=blue&product=green
+ allowReserved: false, // if true, query parameters will be not percent-encoded
+ example: 'Custom example that will be in the documentation and be the default value of the sandbox',
+ ),
+ ];
+ }
+
+ return $description;
+ }
+}
+```
+
+Thanks to [Symfony's automatic service loading](https://symfony.com/doc/current/service_container.html#service-container-services-load-example), which is enabled by default in the API Platform distribution, the filter is automatically registered as a service!
+
+Finally, add this filter to resources you want to be filtered by using the `ApiFilter` attribute:
+
+```php
+getRootAliases()[0];
+ foreach(array_keys($this->getProperties()) as $prop) { // we use array_keys() because getProperties() returns a map of property => strategy
+ if (!$this->isPropertyEnabled($prop, $resourceClass) || !$this->isPropertyMapped($prop, $resourceClass)) {
+ return;
+ }
+ $parameterName = $queryNameGenerator->generateParameterName($prop);
+ $queryBuilder
+ ->andWhere(sprintf('%s.%s LIKE :%s', $rootAlias, $prop, $parameterName))
+ ->setParameter($parameterName, "%" . $value . "%");
+ }
+}
+```
+
+### Manual Service and Attribute Registration
+
+If you don't use Symfony's automatic service loading, you have to register the filter as a service by yourself.
+Use the following service definition (remember, by default, this isn't needed!):
+
+```yaml
+# api/config/services.yaml
+services:
+ # ...
+ # This whole definition can be omitted if automatic service loading is enabled
+ 'App\Filter\RegexpFilter':
+ # The "arguments" key can be omitted if the autowiring is enabled
+ arguments: ['@doctrine', '@?logger']
+ # The "tags" key can be omitted if the autoconfiguration is enabled
+ tags: ['api_platform.filter']
+```
+
+In the previous example, the filter can be applied to 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: ~ }]
+ tags: ['api_platform.filter']
+```
+
+Finally, if you don't want to use the `#[ApiFilter]` attribute, you can register the filter on an API resource class using the `filters` attribute:
+
+```php
+`
+
+Enable the filter:
+
+
+
+```php
+ 'order'])]
+class Tweet
+{
+ // ...
+}
+```
+
+```yaml
+# config/services.yaml
+services:
+ tweet.order_filter:
+ parent: 'api_platform.doctrine.orm.order_filter'
+ arguments:
+ $properties: { id: ~, date: ~ }
+ $orderParameterName: 'order'
+ tags: ['api_platform.filter']
+ # The following are mandatory only if a _defaults section is defined with inverted values.
+ # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
+ autowire: false
+ autoconfigure: false
+ public: false
+
+# config/api/Tweet.yaml
+App\Entity\Tweet:
+ # ...
+ filters: ['tweet.order_filter']
+```
+
+
+
+Given that the collection endpoint is `/tweets`, you can filter tweets by ID and date in ascending or descending order:
+`/tweets?order[id]=asc&order[date]=desc`.
+
+By default, whenever the query does not specify the direction explicitly (e.g: `/tweets?order[id]&order[date]`), filters
+will not be applied unless you configure a default order direction to use:
+
+```php
+ 'asc', 'date' => 'desc'])]
+class Tweet
+{
+ // ...
+}
+```
+
+### Using a Custom Order Query Parameter Name
+
+A conflict will occur if `order` is also the name of a property with the term filter enabled. Luckily, the query
+parameter name to use is configurable:
+
+```yaml
+# api/config/packages/api_platform.yaml
+api_platform:
+ collection:
+ order_parameter_name: '_order' # the URL query parameter to use is now "_order"
+```
+
+## Match Filter
+
+The match filter allows us to find resources that [match](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html)
+the specified text on full-text fields.
+
+Syntax: `?property[]=value`
+
+Enable the filter:
+
+```php
+ $context['filters']['fullName'],
+ 'operator' => 'and',
+ ];
+
+ $requestBody['query']['constant_score']['filter']['bool']['must'][0]['match']['full_name'] = $andQuery;
+
+ return $requestBody;
+ }
+}
+```
diff --git a/core/filters.md b/core/filters.md
index cab3eaecf92..0bb76184480 100644
--- a/core/filters.md
+++ b/core/filters.md
@@ -1,7 +1,7 @@
# Filters
API Platform provides a generic system to apply filters and sort criteria on collections.
-Useful filters for Doctrine ORM, MongoDB ODM, and ElasticSearch are provided with the library.
+Useful filters for Doctrine ORM, Eloquent ORM, MongoDB ODM and ElasticSearch 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 [state providers](state-providers.md) by implementing interfaces provided
@@ -14,6 +14,11 @@ It is also automatically documented as a `search` property for JSON-LD responses

Watch the Filtering & Searching screencast
+For the **specific filters documentation**, please refer to the following pages, depending on your needs:
+- [Doctrine filters documentation](../core/doctrine-filters.md)
+- [Elasticsearch filters documentation](../core/elasticsearch-filters.md)
+- [Laravel filters documentation](../laravel/filters.md)
+
## Parameters
You can declare parameters on a Resource or an Operation through the `parameters` property.
@@ -26,7 +31,7 @@ use ApiPlatform\Metadata\QueryParameter;
// This parameter "page" works only on /books
#[GetCollection(uriTemplate: '/books', parameters: ['page' => new QueryParameter])]
-// This parameter is available on every operations, key is mandatory
+// This parameter is available on every operation, key is mandatory
#[QueryParameter(key: 'q', property: 'freetextQuery')]
class Book {}
```
@@ -64,7 +69,7 @@ class Book {
}
```
-If you don't have autoconfiguration enabled, declare the parameter as a tagged service:
+If you use Symfony, but you don't have autoconfiguration enabled, declare the parameter as a tagged service:
```yaml
services:
@@ -73,8 +78,27 @@ services:
- name: 'api_platform.parameter_provider'
key: 'ApiPlatform\Tests\Fixtures\TestBundle\Parameter\CustomGroupParameterProvider'
```
+or if you are using Laravel tag your provider with:
+
+```php
+app->tag([CustomGroupParameterProvider::class], ParameterProvider::class);
+ }
+}
+```
-### Call a filter
+### Call a filter with Symfony
A Parameter can also call a filter and works on filters that impact the data persistence layer (Doctrine ORM, ODM and Eloquent filters are supported). Let's assume, that we have an Order filter declared:
@@ -108,6 +132,51 @@ class Offer {
}
```
+### Call a filter with Laravel
+
+A Parameter can also call a filter and works on filters that impact the data persistence layer (Doctrine ORM, ODM and Eloquent filters are supported). Let's assume, that we have an Order filter declared:
+
+```php
+app->singleton(OrderFilter::class, function ($app) {
+ return new OrderFilter(['id' => null, 'name' => null], 'order');
+ });
+
+ $this->app->tag([OrderFilter::class], ApiFilter::class);
+ }
+}
+```
+
+We can use this filter specifying we want a query parameter with the `:property` placeholder:
+
+```php
+namespace App\ApiResource;
+
+use ApiPlatform\Metadata\QueryParameter;
+
+#[GetCollection(
+ uriTemplate: 'orders',
+ parameters: [
+ 'order[:property]' => new QueryParameter(filter: 'offer.order_filter'),
+ ]
+)
+class Offer {
+ public string $id;
+ public string $name;
+}
+```
+
### Header parameters
The `HeaderParameter` attribute allows to create a parameter that's using HTTP Headers instead of query parameters:
@@ -163,7 +232,7 @@ class Book {
### Documentation
-A parameter is quite close to its documentation and you can specify the JSON Schema and/or the OpenAPI documentation:
+A parameter is quite close to its documentation, and you can specify the JSON Schema and/or the OpenAPI documentation:
```php
namespace App\ApiResource;
@@ -204,6 +273,8 @@ If you need you can use the `filterContext` to transfer information between a pa
### Parameter validation
+If you use Laravel refers to the [Laravel Validation documentation](../laravel/validation.md).
+
Parameter validation is automatic based on the configuration for example:
```php
@@ -228,10 +299,12 @@ use ApiPlatform\Metadata\QueryParameter;
class ValidateParameter {}
```
-You can also use your own constraint by setting the `constraints` option on a Parameter. In that case we won't setup the automatic validation for you and it'll replace our defaults.
+You can also use your own constraint by setting the `constraints` option on a Parameter. In that case we won't set up the automatic validation for you, and it'll replace our defaults.
### Parameter security
+If you use Laravel refers to the [Laravel Security documentation](../laravel/security.md).
+
Parameters may have security checks:
```php
@@ -250,1663 +323,95 @@ use ApiPlatform\Metadata\QueryParameter;
class SecurityParameter {}
```
-### Decorate a Doctrine filter
-
-A filter that implements the `ApiPlatform\Doctrine\Common\Filter\PropertyAwareFilterInterface` interface can be decorated:
-
-```php
-namespace App\Doctrine\Filter;
-
-use ApiPlatform\Doctrine\Common\Filter\PropertyAwareFilterInterface;
-use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
-use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
-use ApiPlatform\Metadata\Operation;
-use Doctrine\ORM\QueryBuilder;
-use Symfony\Component\DependencyInjection\Attribute\Autowire;
-
-final class SearchTextAndDateFilter implements FilterInterface
-{
- public function __construct(#[Autowire('@api_platform.doctrine.orm.search_filter.instance')] readonly FilterInterface $searchFilter, #[Autowire('@api_platform.doctrine.orm.date_filter.instance')] readonly FilterInterface $dateFilter, protected ?array $properties = null, private array $dateFilterProperties = [], private array $searchFilterProperties = [])
- {
- }
-
- // This function is only used to hook in documentation generators (supported by Swagger and Hydra)
- public function getDescription(string $resourceClass): array
- {
- if ($this->searchFilter instanceof PropertyAwareFilterInterface) {
- $this->searchFilter->setProperties($this->searchFilterProperties);
- }
- if ($this->dateFilter instanceof PropertyAwareFilterInterface) {
- $this->dateFilter->setProperties($this->dateFilterProperties);
- }
-
- return array_merge($this->searchFilter->getDescription($resourceClass), $this->dateFilter->getDescription($resourceClass));
- }
-
- public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
- {
- if ($this->searchFilter instanceof PropertyAwareFilterInterface) {
- $this->searchFilter->setProperties($this->searchFilterProperties);
- }
- if ($this->dateFilter instanceof PropertyAwareFilterInterface) {
- $this->dateFilter->setProperties($this->dateFilterProperties);
- }
-
- $this->searchFilter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $context['filters']['searchOnTextAndDate']] + $context);
- $this->dateFilter->apply($queryBuilder, $queryNameGenerator, $resourceClass, $operation, ['filters' => $context['filters']['searchOnTextAndDate']] + $context);
- }
-}
-```
-
-This can be used with parameters using attributes:
-
-```php
-namespace App\Entity;
-
-use ApiPlatform\Metadata\QueryParameter;
-
-#[GetCollection(
- uriTemplate: 'search_filter_parameter{._format}',
- parameters: [
- 'searchOnTextAndDate[:property]' => new QueryParameter(filter: 'app_filter_date_and_search'),
- ]
-)]
-// Note that we link the parameter filter and this filter using the "alias" option:
-#[ApiFilter(SearchTextAndDateFilter::class, alias: 'app_filter_date_and_search', properties: ['foo', 'createdAt'], arguments: ['dateFilterProperties' => ['createdAt' => 'exclude_null'], 'searchFilterProperties' => ['foo' => 'exact']])]
-#[ORM\Entity]
-class SearchFilterParameter
-{
- /**
- * @var int The id
- */
- #[ORM\Column(type: 'integer')]
- #[ORM\Id]
- #[ORM\GeneratedValue(strategy: 'AUTO')]
- private ?int $id = null;
- #[ORM\Column(type: 'string')]
- private string $foo = '';
-
- #[ORM\Column(type: 'datetime_immutable', nullable: true)]
- private ?\DateTimeImmutable $createdAt = null;
-
- public function getId(): ?int
- {
- return $this->id;
- }
-
- public function getFoo(): string
- {
- return $this->foo;
- }
-
- public function setFoo(string $foo): void
- {
- $this->foo = $foo;
- }
-
- public function getCreatedAt(): ?\DateTimeImmutable
- {
- return $this->createdAt;
- }
-
- public function setCreatedAt(\DateTimeImmutable $createdAt): void
- {
- $this->createdAt = $createdAt;
- }
-}
-```
-
-## Doctrine ORM and MongoDB ODM Filters
-
-!> [!WARNING]
-
-> Prefer using QueryParameter instead of ApiFilter for more flexibility, this is subject to change in the next major version.
-
-### 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 resource declaration, as the `filters` attribute.
-
-For example, having a filter service declaration in `services.yaml`:
-
-```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 with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines.
- autowire: false
- autoconfigure: false
- public: false
-```
-
-Alternatively, you can choose to use a dedicated file to gather filters together:
-
-```yaml
-# api/config/filters.yaml
-services:
- offer.date_filter:
- parent: 'api_platform.doctrine.orm.date_filter'
- arguments: [{ dateProperty: ~ }]
- tags: ['api_platform.filter']
-```
-
-We're linking the filter `offer.date_filter` with the resource like this:
-
-
-
-```php
-
-
-
-
-
-
-
-
- offer.date_filter
-
-
-
-
-
-
-```
-
-
-
-2. By using the `#[ApiFilter]` attribute.
-
-This attribute automatically declares the service, and you just have to use the filter class you want:
-
-```php
-`
-In the following example, we will see how to allow the filtering of a list of e-commerce offers:
+You can add as many groups as you need.
-
+Enable the filter:
```php
'exact', 'price' => 'exact', 'description' => 'partial'])]
-class Offer
-{
- // ...
-}
-```
-
-```yaml
-# config/services.yaml
-services:
- offer.search_filter:
- parent: 'api_platform.doctrine.orm.search_filter'
- arguments: [{ id: 'exact', price: 'exact', description: 'partial' }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.search_filter']
-```
-
-
-
-`http://localhost:8000/api/offers?price=10` will return all offers with a price being exactly `10`.
-`http://localhost:8000/api/offers?description=shirt` will return all offers with a description containing the word "shirt".
-
-Filters can be combined together: `http://localhost:8000/api/offers?price=10&description=shirt`
-
-It is possible to filter on relations too, if `Offer` has a `Product` relation:
-
-
-
-```php
- 'exact'])]
-class Offer
+#[ApiFilter(GroupFilter::class, arguments: ['parameterName' => 'groups', 'overrideDefaultGroups' => false, 'whitelist' => ['allowed_group']])]
+class Book
{
// ...
}
```
-```yaml
-# config/services.yaml
-services:
- offer.search_filter:
- parent: 'api_platform.doctrine.orm.search_filter'
- arguments: [{ product: 'exact' }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.search_filter']
-```
-
-
-
-With this service definition, it is possible to find all offers belonging to the product identified by a given IRI.
-Try the following: `http://localhost:8000/api/offers?product=/api/products/12`.
-Using a numeric ID is also supported: `http://localhost:8000/api/offers?product=12`
+Three arguments are available to configure the filter:
-The above URLs will return all offers for the product having the following IRI as JSON-LD identifier (`@id`): `http://localhost:8000/api/products/12`.
+- `parameterName` is the query parameter name (default `groups`)
+- `overrideDefaultGroups` allows to override the default serialization groups (default `false`)
+- `whitelist` groups whitelist to avoid uncontrolled data exposure (default `null` to allow all groups)
-### Date Filter
+Given that the collection endpoint is `/books`, you can filter by serialization groups with the following query: `/books?groups[]=read&groups[]=write`.
-The date filter allows filtering a collection by date intervals.
+### Property filter
-Syntax: `?property[]=value`
+**Note:** We strongly recommend using [Vulcain](https://vulcain.rocks) instead of this filter.
+Vulcain is faster, allows a better hit rate, and is supported out of the box in the API Platform distribution.
-The value can take any date format supported by the [`\DateTime` constructor](https://www.php.net/manual/en/datetime.construct.php).
+The property filter adds the possibility to select the properties to serialize (sparse fieldsets).
-The `after` and `before` filters will filter including the value whereas `strictly_after` and `strictly_before` will filter excluding the value.
+Syntax: `?properties[]=&properties[][]=`
-Like other filters, the date filter must be explicitly enabled:
+You can add as many properties as you need.
-
+Enable the filter:
```php
'properties', 'overrideDefaultProperties' => false, 'whitelist' => ['allowed_property']])]
+class Book
{
// ...
}
```
-```yaml
-# config/services.yaml
-services:
- offer.date_filter:
- parent: 'api_platform.doctrine.orm.date_filter'
- arguments: [{ createdAt: ~ }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.date_filter']
-```
-
-
-
-Given that the collection endpoint is `/offers`, you can filter offers by date with the following query: `/offers?createdAt[after]=2018-03-19`.
-
-It will return all offers where `createdAt` is superior or equal to `2018-03-19`.
-
-#### Managing `null` Values
-
-The date filter is able to deal with date properties having `null` values.
-Four behaviors are available at the property level of the filter:
-
-| Description | Strategy to set |
-| ------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
-| Use the default behavior of the DBMS | `null` |
-| Exclude items | `ApiPlatform\Doctrine\Common\Filter\DateFilterInterface::EXCLUDE_NULL` (`exclude_null`) |
-| Consider items as oldest | `ApiPlatform\Doctrine\Common\Filter\DateFilterInterface::INCLUDE_NULL_BEFORE` (`include_null_before`) |
-| Consider items as youngest | `ApiPlatform\Doctrine\Common\Filter\DateFilterInterface::INCLUDE_NULL_AFTER` (`include_null_after`) |
-| Always include items | `ApiPlatform\Doctrine\Common\Filter\DateFilterInterface::INCLUDE_NULL_BEFORE_AND_AFTER` (`include_null_before_and_after`) |
+Three arguments are available to configure the filter:
-For instance, exclude entries with a property value of `null` with the following service definition:
+- `parameterName` is the query parameter name (default `properties`)
+- `overrideDefaultProperties` allows to override the default serialization properties (default `false`)
+- `whitelist` properties whitelist to avoid uncontrolled data exposure (default `null` to allow all properties)
-
+Given that the collection endpoint is `/books`, you can filter the serialization properties with the following query: `/books?properties[]=title&properties[]=author`.
+If you want to include some properties of the nested "author" document, use: `/books?properties[]=title&properties[author][]=name`.
-```php
- DateFilterInterface::EXCLUDE_NULL])]
-class Offer
-{
- // ...
-}
-```
+API Platform provides a convenient way to create Doctrine ORM and MongoDB ODM filters. If you use [custom state providers](state-providers.md),
+you can still create filters by implementing the previously mentioned interface, but - as API Platform isn't aware of your
+persistence system's internals - you have to create the filtering logic by yourself.
-```yaml
-# config/services.yaml
-services:
- offer.date_filter:
- parent: 'api_platform.doctrine.orm.date_filter'
- arguments: [{ dateProperty: exclude_null }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.date_filter']
-```
-
-
-
-### Boolean Filter
-
-The boolean filter allows you to search on boolean fields and values.
-
-Syntax: `?property=`
-
-Enable the filter:
-
-
-
-```php
-
-
-Given that the collection endpoint is `/offers`, you can filter offers with the following query: `/offers?isAvailableGenericallyInMyCountry=true`.
-
-It will return all offers where `isAvailableGenericallyInMyCountry` equals `true`.
-
-### Numeric Filter
-
-The numeric filter allows you to search on numeric fields and values.
-
-Syntax: `?property=`
-
-Enable the filter:
-
-
-
-```php
-
-
-Given that the collection endpoint is `/offers`, you can filter offers with the following query: `/offers?sold=1`.
-
-It will return all offers with `sold` equals `1`.
-
-### Range Filter
-
-The range filter allows you to filter by a value lower than, greater than, lower than or equal, greater than or equal and between two values.
-
-Syntax: `?property[]=value`
-
-Enable the filter:
-
-
-
-```php
-
-
-Given that the collection endpoint is `/offers`, you can filter the price with the following query: `/offers?price[between]=12.99..15.99`.
-
-It will return all offers with `price` between 12.99 and 15.99.
-
-You can filter offers by joining two values, for example: `/offers?price[gt]=12.99&price[lt]=19.99`.
-
-### Exists Filter
-
-The "exists" filter allows you to select items based on a nullable field value.
-It will also check the emptiness of a collection association.
-
-Syntax: `?exists[property]=`
-
-Enable the filter:
-
-
-
-```php
-
-
-Given that the collection endpoint is `/offers`, you can filter offers on the nullable field with the following query: `/offers?exists[transportFees]=true`.
-
-It will return all offers where `transportFees` is not `null`.
-
-#### Using a Custom Exists Query Parameter Name
-
-A conflict will occur if `exists` is also the name of a property with the search filter enabled.
-Luckily, the query parameter name to use is configurable:
-
-```yaml
-# api/config/packages/api_platform.yaml
-api_platform:
- collection:
- exists_parameter_name: 'not_null' # the URL query parameter to use is now "not_null"
-```
-
-### Order Filter (Sorting)
-
-The order filter allows sorting a collection against the given properties.
-
-Syntax: `?order[property]=`
-
-Enable the filter:
-
-
-
-```php
- 'order'])]
-class Offer
-{
- // ...
-}
-```
-
-```yaml
-# config/services.yaml
-services:
- offer.order_filter:
- parent: 'api_platform.doctrine.orm.order_filter'
- arguments:
- $properties: { id: ~, name: ~ }
- $orderParameterName: order
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.order_filter']
-```
-
-
-
-Given that the collection endpoint is `/offers`, you can filter offers by name in ascending order and then by ID in descending
-order with the following query: `/offers?order[name]=desc&order[id]=asc`.
-
-By default, whenever the query does not specify the direction explicitly (e.g.: `/offers?order[name]&order[id]`), filters
-will not be applied unless you configure a default order direction to use:
-
-
-
-```php
- 'ASC', 'name' => 'DESC'])]
-class Offer
-{
- // ...
-}
-```
-
-```yaml
-# config/services.yaml
-services:
- offer.order_filter:
- parent: 'api_platform.doctrine.orm.order_filter'
- arguments: [{ id: 'ASC', name: 'DESC' }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.order_filter']
-```
-
-
-
-#### Comparing with Null Values
-
-When the property used for ordering can contain `null` values, you may want to specify how `null` values are treated in
-the comparison:
-
-| Description | Strategy to set |
-| ------------------------------------ |---------------------------------------------------------------------------------------------------|
-| Use the default behavior of the DBMS | `null` |
-| Consider items as smallest | `ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface::NULLS_SMALLEST` (`nulls_smallest`) |
-| Consider items as largest | `ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface::NULLS_LARGEST` (`nulls_largest`) |
-| Order items always first | `ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface::NULLS_ALWAYS_FIRST` (`nulls_always_first`) |
-| Order items always last | `ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface::NULLS_ALWAYS_LAST` (`nulls_always_last`) |
-
-For instance, treat entries with a property value of `null` as the smallest, with the following service definition:
-
-
-
-```php
- ['nulls_comparison' => OrderFilterInterface::NULLS_SMALLEST, 'default_direction' => 'DESC']])]
-class Offer
-{
- // ...
-}
-```
-
-```yaml
-# config/services.yaml
-services:
- offer.order_filter:
- parent: 'api_platform.doctrine.orm.order_filter'
- arguments:
- [
- {
- validFrom:
- { nulls_comparison: 'nulls_smallest', default_direction: 'DESC' },
- },
- ]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.order_filter']
-```
-
-
-
-The strategy to use by default can be configured globally:
-
-```yaml
-# api/config/packages/api_platform.yaml
-api_platform:
- collection:
- order_nulls_comparison: 'nulls_smallest'
-```
-
-#### Using a Custom Order Query Parameter Name
-
-A conflict will occur if `order` is also the name of a property with the search filter enabled.
-Luckily, the query parameter name to use is configurable:
-
-```yaml
-# api/config/packages/api_platform.yaml
-api_platform:
- collection:
- order_parameter_name: '_order' # the URL query parameter to use is now "_order"
-```
-
-### Filtering on Nested Properties
-
-Sometimes, you need to be able to perform filtering based on some linked resources (on the other side of a relation). All
-built-in filters support nested properties using the dot (`.`) syntax, e.g.:
-
-
-
-```php
- 'exact'])]
-class Offer
-{
- // ...
-}
-```
-
-```yaml
-# config/services.yaml
-services:
- offer.order_filter:
- parent: 'api_platform.doctrine.orm.order_filter'
- arguments: [{ product.releaseDate: ~ }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
- offer.search_filter:
- parent: 'api_platform.doctrine.orm.search_filter'
- arguments: [{ product.color: 'exact' }]
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Offer.yaml
-App\Entity\Offer:
- # ...
- operations:
- ApiPlatform\Metadata\GetCollection:
- filters: ['offer.order_filter', 'offer.search_filter']
-```
-
-
-
-The above allows you to find offers by their respective product's color: `http://localhost:8000/api/offers?product.color=red`,
-or order offers by the product's release date: `http://localhost:8000/api/offers?order[product.releaseDate]=desc`
-
-### Enabling a Filter for All Properties of a Resource
-
-As we have seen in previous examples, properties where filters can be applied must be explicitly declared. If you don't
-care about security and performance (e.g. an API with restricted access), it is also possible to enable built-in filters
-for all properties:
-
-
-
-```php
-
-
-**Note: Filters on nested properties must still be enabled explicitly, in order to keep things sane.**
-
-Regardless of this option, filters can be applied on a property only if:
-
-- the property exists
-- the value is supported (ex: `asc` or `desc` for the order filters).
-
-It means that the filter will be **silently** ignored if the property:
-
-- does not exist
-- is not enabled
-- has an invalid value
-
-## Elasticsearch Filters
-
-### Ordering Filter (Sorting)
-
-The order filter allows to [sort](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-sort.html)
-a collection against the given properties.
-
-Syntax: `?order[property]=`
-
-Enable the filter:
-
-
-
-```php
- 'order'])]
-class Tweet
-{
- // ...
-}
-```
-
-```yaml
-# config/services.yaml
-services:
- tweet.order_filter:
- parent: 'api_platform.doctrine.orm.order_filter'
- arguments:
- $properties: { id: ~, date: ~ }
- $orderParameterName: 'order'
- tags: ['api_platform.filter']
- # The following are mandatory only if a _defaults section is defined with inverted values.
- # You may want to isolate filters in a dedicated file to avoid adding the following lines (by adding them in the defaults section)
- autowire: false
- autoconfigure: false
- public: false
-
-# config/api/Tweet.yaml
-App\Entity\Tweet:
- # ...
- filters: ['tweet.order_filter']
-```
-
-
-
-Given that the collection endpoint is `/tweets`, you can filter tweets by ID and date in ascending or descending order:
-`/tweets?order[id]=asc&order[date]=desc`.
-
-By default, whenever the query does not specify the direction explicitly (e.g: `/tweets?order[id]&order[date]`), filters
-will not be applied unless you configure a default order direction to use:
-
-```php
- 'asc', 'date' => 'desc'])]
-class Tweet
-{
- // ...
-}
-```
-
-#### Using a Custom Order Query Parameter Name (Elastic)
-
-A conflict will occur if `order` is also the name of a property with the term filter enabled. Luckily, the query
-parameter name to use is configurable:
-
-```yaml
-# api/config/packages/api_platform.yaml
-api_platform:
- collection:
- order_parameter_name: '_order' # the URL query parameter to use is now "_order"
-```
-
-### Match Filter
-
-The match filter allows us to find resources that [match](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html)
-the specified text on full-text fields.
-
-Syntax: `?property[]=value`
-
-Enable the filter:
-
-```php
-`
-
-You can add as many groups as you need.
-
-Enable the filter:
-
-```php
- 'groups', 'overrideDefaultGroups' => false, 'whitelist' => ['allowed_group']])]
-class Book
-{
- // ...
-}
-```
-
-Three arguments are available to configure the filter:
-
-- `parameterName` is the query parameter name (default `groups`)
-- `overrideDefaultGroups` allows to override the default serialization groups (default `false`)
-- `whitelist` groups whitelist to avoid uncontrolled data exposure (default `null` to allow all groups)
-
-Given that the collection endpoint is `/books`, you can filter by serialization groups with the following query: `/books?groups[]=read&groups[]=write`.
-
-### Property filter
-
-**Note:** We strongly recommend using [Vulcain](https://vulcain.rocks) instead of this filter.
-Vulcain is faster, allows a better hit rate, and is supported out of the box in the API Platform distribution.
-
-The property filter adds the possibility to select the properties to serialize (sparse fieldsets).
-
-Syntax: `?properties[]=&properties[][]=`
-
-You can add as many properties as you need.
-
-Enable the filter:
-
-```php
- 'properties', 'overrideDefaultProperties' => false, 'whitelist' => ['allowed_property']])]
-class Book
-{
- // ...
-}
-```
-
-Three arguments are available to configure the filter:
-
-- `parameterName` is the query parameter name (default `properties`)
-- `overrideDefaultProperties` allows to override the default serialization properties (default `false`)
-- `whitelist` properties whitelist to avoid uncontrolled data exposure (default `null` to allow all properties)
-
-Given that the collection endpoint is `/books`, you can filter the serialization properties with the following query: `/books?properties[]=title&properties[]=author`.
-If you want to include some properties of the nested "author" document, use: `/books?properties[]=title&properties[author][]=name`.
-
-## Creating Custom Filters
-
-Custom filters can be written by implementing the `ApiPlatform\Api\FilterInterface` interface.
-
-API Platform provides a convenient way to create Doctrine ORM and MongoDB ODM filters. If you use [custom state providers](state-providers.md),
-you can still create filters by implementing the previously mentioned interface, but - as API Platform isn't aware of your
-persistence system's internals - you have to create the filtering logic by yourself.
-
-### Creating Custom Doctrine ORM Filters
-
-Doctrine ORM filters have access to the context created from the HTTP request and to the `QueryBuilder` instance used to
-retrieve data from the database. They are only applied to collections. If you want to deal with the DQL query generated
-to retrieve items, [extensions](extensions.md) are the way to go.
-
-A Doctrine ORM filter is basically a class implementing the `ApiPlatform\Doctrine\Orm\Filter\FilterInterface`.
-API Platform includes a convenient abstract class implementing this interface and providing utility methods: `ApiPlatform\Doctrine\Orm\Filter\AbstractFilter`.
-
-In the following example, we create a class to filter a collection by applying a regular expression to a property.
-The `REGEXP` DQL function used in this example can be found in the [`DoctrineExtensions`](https://github.com/beberlei/DoctrineExtensions)
-library. This library must be properly installed and registered to use this example (works only with MySQL).
-
-```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' => Type::BUILTIN_TYPE_STRING,
- 'required' => false,
- 'description' => 'Filter using a regex. This will appear in the OpenApi documentation!',
- 'openapi' => new Parameter(
- name: $property,
- in: 'query',
- allowEmptyValue: true,
- explode: false, // to be true, the type must be Type::BUILTIN_TYPE_ARRAY, ?product=blue,green will be ?product=blue&product=green
- allowReserved: false, // if true, query parameters will be not percent-encoded
- example: 'Custom example that will be in the documentation and be the default value of the sandbox',
- ),
- ];
- }
-
- return $description;
- }
-}
-```
-
-Thanks to [Symfony's automatic service loading](https://symfony.com/doc/current/service_container.html#service-container-services-load-example), which is enabled by default in the API Platform distribution, the filter is automatically registered as a service!
-
-Finally, add this filter to resources you want to be filtered by using the `ApiFilter` attribute:
-
-```php
-getRootAliases()[0];
- foreach(array_keys($this->getProperties()) as $prop) { // we use array_keys() because getProperties() returns a map of property => strategy
- if (!$this->isPropertyEnabled($prop, $resourceClass) || !$this->isPropertyMapped($prop, $resourceClass)) {
- return;
- }
- $parameterName = $queryNameGenerator->generateParameterName($prop);
- $queryBuilder
- ->andWhere(sprintf('%s.%s LIKE :%s', $rootAlias, $prop, $parameterName))
- ->setParameter($parameterName, "%" . $value . "%");
- }
-}
-```
-
-#### Manual Service and Attribute Registration
-
-If you don't use Symfony's automatic service loading, you have to register the filter as a service by yourself.
-Use the following service definition (remember, by default, this isn't needed!):
-
-```yaml
-# api/config/services.yaml
-services:
- # ...
- # This whole definition can be omitted if automatic service loading is enabled
- 'App\Filter\RegexpFilter':
- # The "arguments" key can be omitted if the autowiring is enabled
- arguments: ['@doctrine', '@?logger']
- # The "tags" key can be omitted if the autoconfiguration is enabled
- tags: ['api_platform.filter']
-```
-
-In the previous example, the filter can be applied to 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: ~ }]
- tags: ['api_platform.filter']
-```
-
-Finally, if you don't want to use the `#[ApiFilter]` attribute, you can register the filter on an API resource class using the `filters` attribute:
-
-```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](https://www.doctrine-project.org/projects/doctrine-orm/en/current/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 to 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](https://www.michaelperrin.fr/blog/2014/12/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
-getReflectionClass()->getAttributes(UserAware::class)[0] ?? null;
-
- $fieldName = $userAware?->getArguments()['userFieldName'] ?? null;
- if ($fieldName === '' || is_null($fieldName)) {
- return '';
- }
-
- 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);
- }
-}
-```
-
-Now, we must configure the Doctrine filter.
-
-```yaml
-# api/config/packages/api_platform.yaml
-doctrine:
- orm:
- filters:
- user_filter:
- class: App\Filter\UserFilter
- enabled: true
-```
+If you need more information about creating custom filters, refer to the following documentation:
-Done: Doctrine will automatically filter all `UserAware`entities!
+- [Creating Custom Doctrine ORM filters](../core/doctrine-filters.md#creating-custom-doctrine-orm-filters)
+- [Creating Custom Doctrine Mongo ODM filters](../core/doctrine-filters.md#creating-custom-doctrine-mongodb-odm-filters)
+- [Creating Custom Doctrine ORM filters](../core/elasticsearch-filters.md#creating-custom-elasticsearch-filters)
## ApiFilter Attribute
diff --git a/outline.yaml b/outline.yaml
index 27bbac705d6..db9b30b5dd5 100644
--- a/outline.yaml
+++ b/outline.yaml
@@ -29,6 +29,8 @@ chapters:
- state-providers
- state-processors
- filters
+ - elasticsearch-filters
+ - doctrine-filters
- subresources
- serialization
- validation