API Platform version(s) affected: 4.2.17 - 4.3.17
Description
When an operation uses a polymorphic input DTO - an abstract input class that is not itself #[ApiResource] and carries a #[Serializer\DiscriminatorMap] - a constructor argument that is declared only on a concrete subclass and is object-typed is passed to the constructor as the raw array instead of being denormalized into the object, causing a TypeError (HTTP 500).
Introduced by #7779 (shipped in 4.2.17). Confirmed still present on 4.3.17, the latest stable. Not affected: <= 4.2.16.
All of the following must hold to trigger it:
- An operation uses
input: <SomeClass>::class where that class is not itself #[ApiResource] (a plain DTO).
- That input base carries a
#[Serializer\DiscriminatorMap] (polymorphic input).
- A mapped concrete subclass declares a constructor argument that is both (a) not a property on the abstract base, and (b) typed as an object / nested DTO (not a scalar or array).
- The request payload supplies that field as a nested object.
How to reproduce
use Symfony\Component\Serializer\Attribute\DiscriminatorMap;
// Polymorphic input base - a plain DTO, NOT #[ApiResource].
#[DiscriminatorMap(typeProperty: 'type', mapping: [
'webhook' => WebhookChannelInput::class,
])]
abstract class NotificationChannelInput
{
}
final class WebhookChannelInput extends NotificationChannelInput
{
public function __construct(
public readonly string $type,
// Object-typed, declared ONLY on this subclass - the trigger.
public readonly WebhookConfig $config,
) {
}
}
final class WebhookConfig
{
public function __construct(
public readonly string $url,
public readonly int $retries = 3,
) {
}
}
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Post;
#[ApiResource(operations: [
new Post(
uriTemplate: '/notification_channels',
input: NotificationChannelInput::class,
// any processor / output
),
])]
final class ChannelResource
{
// ...
}
Request:
POST /notification_channels
Content-Type: application/json
{
"type": "webhook",
"config": { "url": "https://example.com/hook", "retries": 5 }
}
Expected: WebhookChannelInput with $config denormalized into a WebhookConfig instance.
Actual: HTTP 500 - WebhookChannelInput::__construct(): Argument #2 ($config) must be of type WebhookConfig, array given
API Platform version(s) affected: 4.2.17 - 4.3.17
Description
When an operation uses a polymorphic input DTO - an abstract input class that is not itself
#[ApiResource]and carries a#[Serializer\DiscriminatorMap]- a constructor argument that is declared only on a concrete subclass and is object-typed is passed to the constructor as the raw array instead of being denormalized into the object, causing a TypeError (HTTP 500).Introduced by #7779 (shipped in 4.2.17). Confirmed still present on 4.3.17, the latest stable. Not affected: <= 4.2.16.
All of the following must hold to trigger it:
input: <SomeClass>::classwhere that class is not itself#[ApiResource](a plain DTO).#[Serializer\DiscriminatorMap](polymorphic input).How to reproduce
Request:
Expected:
WebhookChannelInputwith$configdenormalized into aWebhookConfiginstance.Actual: HTTP 500 -
WebhookChannelInput::__construct(): Argument #2 ($config) must be of type WebhookConfig, array given