Skip to content

feat(doctrine): add ChainFilter to compose filters on one parameter#8416

Open
soyuka wants to merge 1 commit into
api-platform:4.4from
soyuka:feature/chain-filter
Open

feat(doctrine): add ChainFilter to compose filters on one parameter#8416
soyuka wants to merge 1 commit into
api-platform:4.4from
soyuka:feature/chain-filter

Conversation

@soyuka

@soyuka soyuka commented Jul 22, 2026

Copy link
Copy Markdown
Member
Q A
Branch? 4.4
Bug fix? no
New feature? yes
Deprecations? no
Issues Related to #8407
License MIT
Doc PR todo

What

Adds ApiPlatform\Doctrine\Orm\Filter\ChainFilter — a thin composite that holds several filters and applies each in turn on a single query-parameter key:

new QueryParameter(
    key: 'quantity',
    filter: new ChainFilter([
        new ExactFilter(),
        new ComparisonFilter(new ExactFilter()),
    ]),
    property: 'quantity',
)
  • ?quantity=10 → equality
  • ?quantity[lt]=10 / [lte] / [gt] / [gte] / [ne] → comparison

Each primary self-selects by value shapeComparisonFilter ignores scalars, ExactFilter/PartialSearchFilter ignore associative (operator-map) arrays — so the composite just applies all of them; no supports() method needed. getOpenApiParameters() merges every wrapped filter's parameters, so the docs show the plain field and the operator variants. ManagerRegistry/Logger are forwarded to aware inner filters, mirroring FreeTextQueryFilter.

Why

Users combining equality with comparison/date operators on one property previously had to hand-write a bespoke dispatching decorator (see #8407). ChainFilter makes it declarative and reusable, staying on the "thin composers over single-responsibility primaries" line of the new filter system (like OrFilter, FreeTextQueryFilter).

Note on the guard

This PR also carries the associative-array guard on ExactFilter/PartialSearchFilter (if (\is_array($value) && !array_is_list($value)) return;). That guard is load-bearing for the chain — without it, ExactFilter treats an operator-map as an IN (...) list and corrupts the comparison. The same guard ships on 4.3 in #8415; the two will reconcile cleanly on merge-up (identical hunk). If you prefer, merge #8415 up first and I'll rebase this to drop the duplicate.

Test

tests/Functional/Parameters/ChainFilterTest.php: exact match, [lt], [gte], and an OpenAPI assertion that both filters' parameters are merged. Full tests/Functional/Parameters/ suite green (447 tests), php-cs-fixer clean, PHPStan [OK].

ChainFilter applies each wrapped filter in turn so a single query-parameter key
can accept several value shapes — e.g. equality plus comparison operators —
without a bespoke decorator. Each primary self-selects by value shape
(ComparisonFilter ignores scalars; ExactFilter/PartialSearchFilter ignore
operator-map arrays), so the composite applies all of them and merges their
OpenAPI parameters. ManagerRegistry/Logger are forwarded to aware inner filters,
mirroring FreeTextQueryFilter.

Also adds the associative-array guard on ExactFilter/PartialSearchFilter, which
is load-bearing for the chain (same guard as api-platform#8415 on 4.3; will reconcile on
merge-up).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant