@@ -120,9 +120,10 @@ services all begin with `api_platform.doctrine_mongodb.odm`.
120120
121121# # Search Filter
122122
123- > [!WARNING] The SearchFilter is a multi-type filter that may have inconsistencies (eg: you can
124- > search a partial date with LIKE) we recommend to use type-specific filters such as
125- > `PartialSearchFilter` or `DateFilter` instead.
123+ > [!WARNING] The SearchFilter is a multi-type filter that may have inconsistencies (e.g., you can
124+ > search a partial date with LIKE). We recommend using type-specific filters such as `ExactFilter`,
125+ > `PartialSearchFilter`, `ComparisonFilter`, or `IriFilter` instead. See the
126+ > [migration guide](#migrating-from-apifilter-to-queryparameter).
126127
127128# ## Built-in Search Filters since API Platform >= 4.2
128129
@@ -135,11 +136,11 @@ To add some search filters, choose over this new list:
135136 notation)
136137- [PartialSearchFilter](#partial-search-filter) (filter using a `LIKE %value%`; supports nested
137138 properties via dot notation)
139+ - [ComparisonFilter](#comparison-filter) (filter with comparison operators `gt`, `gte`, `lt`, `lte`,
140+ ` ne` ; replaces `DateFilter`, `NumericFilter`, and `RangeFilter`)
138141- [FreeTextQueryFilter](#free-text-query-filter) (allows you to apply multiple filters to multiple
139142 properties of a resource at the same time, using a single parameter in the URL)
140- - [OrFilter](#or-filter) (apply a filter using `orWhere` instead of `andWhere` )
141- - [ComparisonFilter](#comparison-filter) (add `gt`, `gte`, `lt`, `lte`, `ne` operators to an
142- equality or UUID filter)
143+ - [OrFilter](#or-filter) (apply a filter using `orWhere` instead of `andWhere`)
143144
144145# ## SearchFilter
145146
@@ -559,6 +560,11 @@ parameter key, one per operator. For a parameter named `price`, the generated pa
559560
560561# # Date Filter
561562
563+ > [!TIP] Consider using [`ComparisonFilter`](#comparison-filter) wrapping `ExactFilter` as a modern
564+ > replacement. `ComparisonFilter` does not extend `AbstractFilter`, works natively with
565+ > `QueryParameter`, and supports the same date comparison use cases with `gt`, `gte`, `lt`, `lte`
566+ > operators.
567+
562568The date filter allows filtering a collection by date intervals.
563569
564570Syntax : ` ?property[<after|before|strictly_after|strictly_before>]=value`
@@ -717,6 +723,9 @@ class Offer
717723
718724# # Boolean Filter
719725
726+ > [!TIP] Consider using [`ExactFilter`](#exact-filter) as a modern replacement. `ExactFilter` does
727+ > not extend `AbstractFilter` and works natively with `QueryParameter`.
728+
720729The boolean filter allows you to search on boolean fields and values.
721730
722731Syntax : ` ?property=<true|false|1|0>`
@@ -758,6 +767,11 @@ It will return all offers where `isAvailableGenericallyInMyCountry` equals `true
758767
759768# # Numeric Filter
760769
770+ > [!TIP] For comparison operations on numeric fields, consider using
771+ > [`ComparisonFilter`](#comparison-filter) wrapping `ExactFilter`. `ComparisonFilter` does not
772+ > extend `AbstractFilter`, works natively with `QueryParameter`, and provides `gt`, `gte`, `lt`,
773+ > `lte`, and `ne` operators. For exact numeric matching, `ExactFilter` alone is sufficient.
774+
761775The numeric filter allows you to search on numeric fields and values.
762776
763777Syntax : ` ?property=<int|bigint|decimal...>`
@@ -799,6 +813,11 @@ It will return all offers with `sold` equals `1`.
799813
800814# # Range Filter
801815
816+ > [!TIP] Consider using [`ComparisonFilter`](#comparison-filter) wrapping `ExactFilter` as a modern
817+ > replacement. `ComparisonFilter` does not extend `AbstractFilter`, works natively with
818+ > `QueryParameter`, and supports range queries by combining `gte` and `lte` operators (e.g.,
819+ > `?price[gte]=10&price[lte]=100`).
820+
802821The range filter allows you to filter by a value lower than, greater than, lower than or equal,
803822greater than or equal and between two values.
804823
@@ -900,6 +919,9 @@ api_platform:
900919
901920# # Order Filter (Sorting)
902921
922+ > [!TIP] Consider using [`SortFilter`](#sort-filter) as a modern replacement. `SortFilter` does not
923+ > extend `AbstractFilter` and works natively with `QueryParameter`.
924+
903925The order filter allows sorting a collection against the given properties.
904926
905927Syntax : ` ?order[property]=<asc|desc>`
@@ -1272,6 +1294,154 @@ class Employee
12721294}
12731295` ` `
12741296
1297+ # # Migrating from ApiFilter to QueryParameter
1298+
1299+ API Platform 4.2+ introduces a new generation of filters designed to work natively with
1300+ ` QueryParameter` . These filters do not extend `AbstractFilter` and avoid the issues that arise when
1301+ legacy filters are instantiated with `new` inside an attribute (missing `ManagerRegistry`,
1302+ ` NameConverter` , `Logger`).
1303+
1304+ The following table shows how to replace each legacy filter. All modern replacements are available
1305+ for both Doctrine ORM (`ApiPlatform\Doctrine\Orm\Filter\*`) and MongoDB ODM
1306+ (`ApiPlatform\Doctrine\Odm\Filter\*`).
1307+
1308+ | Legacy filter (`AbstractFilter`) | Modern replacement |
1309+ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
1310+ | `SearchFilter` (exact strategy) | [`ExactFilter`](#exact-filter) |
1311+ | `SearchFilter` (partial, start, end, word_start strategies) | [`PartialSearchFilter`](#partial-search-filter) |
1312+ | `SearchFilter` (relations / IRI matching) | [`IriFilter`](#iri-filter) |
1313+ | `BooleanFilter` | [`ExactFilter`](#exact-filter) |
1314+ | `DateFilter` | [`ComparisonFilter(new ExactFilter())`](#comparison-filter) |
1315+ | `NumericFilter` | [`ExactFilter`](#exact-filter) (exact) or [`ComparisonFilter(new ExactFilter())`](#comparison-filter) (range) |
1316+ | `RangeFilter` | [`ComparisonFilter(new ExactFilter())`](#comparison-filter) |
1317+ | `OrderFilter` | [`SortFilter`](#sort-filter) |
1318+ | `ExistsFilter` | No modern replacement yet — keep using `ExistsFilter` |
1319+
1320+ # ## Example: Migrating a DateFilter
1321+
1322+ Before (legacy) :
1323+
1324+ ` ` ` php
1325+ <?php
1326+ use ApiPlatform\D octrine\O rm\F ilter\D ateFilter;
1327+ use ApiPlatform\M etadata\A piFilter;
1328+ use ApiPlatform\M etadata\A piResource;
1329+
1330+ #[ApiResource]
1331+ #[ApiFilter(DateFilter::class, properties: ['createdAt'])]
1332+ class Offer
1333+ {
1334+ // ...
1335+ }
1336+ ` ` `
1337+
1338+ After (modern) :
1339+
1340+ ` ` ` php
1341+ <?php
1342+ use ApiPlatform\D octrine\O rm\F ilter\C omparisonFilter;
1343+ use ApiPlatform\D octrine\O rm\F ilter\E xactFilter;
1344+ use ApiPlatform\M etadata\A piResource;
1345+ use ApiPlatform\M etadata\G etCollection;
1346+ use ApiPlatform\M etadata\Q ueryParameter;
1347+
1348+ #[ApiResource]
1349+ #[GetCollection(
1350+ parameters: [
1351+ 'createdAt' => new QueryParameter(
1352+ filter: new ComparisonFilter(new ExactFilter()),
1353+ property: 'createdAt',
1354+ ),
1355+ ],
1356+ )]
1357+ class Offer
1358+ {
1359+ // ...
1360+ }
1361+ ` ` `
1362+
1363+ The query syntax changes from `?createdAt[after]=2025-01-01` to `?createdAt[gte]=2025-01-01`.
1364+
1365+ # ## Example: Migrating a RangeFilter
1366+
1367+ Before (legacy) :
1368+
1369+ ` ` ` php
1370+ <?php
1371+ use ApiPlatform\D octrine\O rm\F ilter\R angeFilter;
1372+ use ApiPlatform\M etadata\A piFilter;
1373+ use ApiPlatform\M etadata\A piResource;
1374+
1375+ #[ApiResource]
1376+ #[ApiFilter(RangeFilter::class, properties: ['price'])]
1377+ class Product
1378+ {
1379+ // ...
1380+ }
1381+ ` ` `
1382+
1383+ After (modern) :
1384+
1385+ ` ` ` php
1386+ <?php
1387+ use ApiPlatform\D octrine\O rm\F ilter\C omparisonFilter;
1388+ use ApiPlatform\D octrine\O rm\F ilter\E xactFilter;
1389+ use ApiPlatform\M etadata\A piResource;
1390+ use ApiPlatform\M etadata\G etCollection;
1391+ use ApiPlatform\M etadata\Q ueryParameter;
1392+
1393+ #[ApiResource]
1394+ #[GetCollection(
1395+ parameters: [
1396+ 'price' => new QueryParameter(
1397+ filter: new ComparisonFilter(new ExactFilter()),
1398+ property: 'price',
1399+ ),
1400+ ],
1401+ )]
1402+ class Product
1403+ {
1404+ // ...
1405+ }
1406+ ` ` `
1407+
1408+ The query syntax changes from `?price[between]=10..100` to `?price[gte]=10&price[lte]=100`.
1409+
1410+ # ## MongoDB ODM
1411+
1412+ The migration works the same way for MongoDB ODM — just use the ODM namespace :
1413+
1414+ ` ` ` php
1415+ <?php
1416+ use ApiPlatform\D octrine\O dm\F ilter\C omparisonFilter;
1417+ use ApiPlatform\D octrine\O dm\F ilter\E xactFilter;
1418+ use ApiPlatform\M etadata\A piResource;
1419+ use ApiPlatform\M etadata\G etCollection;
1420+ use ApiPlatform\M etadata\Q ueryParameter;
1421+
1422+ #[ApiResource]
1423+ #[GetCollection(
1424+ parameters: [
1425+ 'createdAt' => new QueryParameter(
1426+ filter: new ComparisonFilter(new ExactFilter()),
1427+ property: 'createdAt',
1428+ ),
1429+ ],
1430+ )]
1431+ class Event
1432+ {
1433+ // ...
1434+ }
1435+ ` ` `
1436+
1437+ The same modern filters are available for both ORM and ODM : ` ExactFilter` , `PartialSearchFilter`,
1438+ ` ComparisonFilter` , `SortFilter`, and `IriFilter`.
1439+
1440+ > [!NOTE] Legacy filters extending `AbstractFilter` still work with `QueryParameter` but may have
1441+ > issues with `nameConverter` when properties use camelCase names. If you encounter silent filter
1442+ > failures with camelCase properties (e.g., `createdAt`, `firstName`), upgrading to the modern
1443+ > filter equivalents listed above is the recommended solution.
1444+
12751445# # Filtering on Nested Properties
12761446
12771447Parameter-based filters (`QueryParameter`) support nested/related properties via dot notation. The
0 commit comments