Skip to content

fix(metadata): :property dedup drops repeated parameters#8196

Merged
soyuka merged 1 commit into
api-platform:4.3from
soyuka:fix/parameter-property-placeholder-deduplication
May 22, 2026
Merged

fix(metadata): :property dedup drops repeated parameters#8196
soyuka merged 1 commit into
api-platform:4.3from
soyuka:fix/parameter-property-placeholder-deduplication

Conversation

@soyuka
Copy link
Copy Markdown
Member

@soyuka soyuka commented May 21, 2026

Q A
Branch? 4.2
Tickets Closes #8184
License MIT

Summary

Multiple #[QueryParameter(key: ':property', ...)] class-level attributes with disjoint properties lists were silently collapsed to the last attribute. Same key, different filter per property was unreachable without awkward namespacing (exact[:property], partial[:property]).

Example

#[ApiResource]
#[QueryParameter(key: ':property', filter: new ExactFilter(), properties: ['field1', 'field2'])]
#[QueryParameter(key: ':property', filter: new BooleanFilter(), properties: ['field3', 'field4'])]
class Task { /* ... */ }

Before this fix only the second attribute survived; after the fix all four properties resolve to their declared filters.

Why

Parameters::add(), Parameters::__construct(), and MetadataCollectionFactoryTrait::mergeOperationParameters() deduplicated by (class, key) before the :property placeholder was expanded. ParameterResourceMetadataCollectionFactory::getProperties() cache key was also shared between disjoint templates.

:property is a template, not a final key — it expands per-property in ParameterResourceMetadataCollectionFactory::getDefaultParameters. Two templates with disjoint properties lists must coexist; the final per-property keys are concrete and dedup normally.

What changed

  • Parameters::add / Parameters::__construct: when key contains :property, the storage key includes a discriminator built from the parameter's properties (or property) list. Deterministic, cache-safe.
  • MetadataCollectionFactoryTrait::mergeOperationParameters: skip the has() short-circuit for :property templated keys.
  • ParameterResourceMetadataCollectionFactory::getProperties: include properties list in the local cache key.

Templates with identical properties keep last-write-wins (intentional override).

Tests

  • Metadata\Tests\ParametersTest: two new tests cover constructor and add() paths.
  • Metadata\Tests\Resource\Factory\ParameterResourceMetadataCollectionFactoryTest::testRepeatedPropertyPlaceholderAttributesExpandPerPropertyFilter: end-to-end factory expansion verifying each property receives its declared filter.

Related

#7870 (per-property Parameter attributes via TARGET_PROPERTY) provides a complementary, cleaner declaration model for the original use case in #8184 once it lands.

Multiple `#[QueryParameter(key: ':property', ...)]` attributes with
disjoint properties lists were silently overwritten by the last one in
`Parameters::add`, `Parameters::__construct` and
`MetadataCollectionFactoryTrait::mergeOperationParameters`. The
`getProperties()` cache in `ParameterResourceMetadataCollectionFactory`
also collided on shared key.

`:property` keys are templates expanded per-property later, so multiple
templates with disjoint `properties` must coexist. Templates with
identical `properties` keep last-write-wins semantics.

Closes api-platform#8184
@soyuka soyuka force-pushed the fix/parameter-property-placeholder-deduplication branch from c43fc5b to 5d51ad9 Compare May 22, 2026 11:34
@soyuka soyuka changed the base branch from 4.2 to 4.3 May 22, 2026 11:59
@soyuka soyuka merged commit 53d8f56 into api-platform:4.3 May 22, 2026
120 checks passed
@soyuka soyuka deleted the fix/parameter-property-placeholder-deduplication branch May 22, 2026 12:01
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