Skip to content

fix(serializer): denormalize object-typed args on discriminated input DTO subclass#8424

Open
soyuka wants to merge 1 commit into
api-platform:4.3from
soyuka:fix/discriminated-input-dto-8414
Open

fix(serializer): denormalize object-typed args on discriminated input DTO subclass#8424
soyuka wants to merge 1 commit into
api-platform:4.3from
soyuka:fix/discriminated-input-dto-8414

Conversation

@soyuka

@soyuka soyuka commented Jul 26, 2026

Copy link
Copy Markdown
Member
Q A
Branch? 4.3
Tickets Fixes #8414
License MIT
Doc PR n/a

Problem

When an operation uses a plain (non-#[ApiResource]) input DTO carrying a Symfony Serializer #[DiscriminatorMap], and a concrete subclass declares a constructor argument that is (a) not a property on the abstract base and (b) object-typed (a nested DTO, not a scalar/array), that argument is passed to the constructor as the raw array instead of being denormalized — TypeError, HTTP 500.

Regression from #7779 (shipped 4.2.17); not affected <= 4.2.16.

WebhookChannelInput::__construct(): Argument #2 ($config) must be of type WebhookConfig, array given

Root cause

In AbstractItemNormalizer::denormalize(), the input-DTO path (#7779) pins resource_class to the abstract input base. The discriminator then resolves the concrete subclass for instantiation, but because the resolved class is not itself a resource, resource_class stays the abstract base. Constructor/setter argument metadata is looked up against that base (createAndValidateAttributeValue()), so a subclass-only property resolves to no type and its nested payload falls through as a raw array.

The existing CrudAbstractTest::testCreateConcreteViaDiscriminatorOnAbstract doesn't catch this: there the abstract base is a resource, so the pre-existing isResourceClass() branch already re-pins resource_class to the concrete resource.

Fix

On the input path, when the discriminator resolves a concrete non-resource subclass, pin resource_class to that concrete class so subclass-only argument types are found and denormalized. Sits next to the existing isResourceClass() branch that already handles the resource case.

Tests

  • New functional test DiscriminatedInputDtoTest mirroring the issue (plain-DTO input + Serializer DiscriminatorMap + object-typed subclass-only constructor arg). Fails with the exact TypeError/500 before the fix, 201 after.
  • CrudAbstractTest (Doctrine table-inheritance discriminator) and the AbstractItemNormalizer unit suite stay green.

… DTO subclass

When an operation uses a plain (non-ApiResource) input DTO carrying a
Serializer DiscriminatorMap, the normalizer pins resource_class to the
abstract input base. The discriminator resolves the concrete subclass for
instantiation, but constructor/setter argument metadata was still read from
the abstract base, so an object-typed argument declared only on the subclass
resolved to no type and its nested payload was passed as a raw array,
triggering a TypeError (HTTP 500).

Pin resource_class to the discriminator-resolved concrete class on the input
path so subclass-only argument types are found and denormalized.

Regression from api-platform#7779 (4.2.17).

Fixes api-platform#8414
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