Skip to content

Nullable arrays of BackedEnums or Objects are not properly deserialized #8379

Description

@MariusJam

API Platform version(s) affected: ^4.2

Description
When using Symfony 7.1+ and API Platform 4.2+, properties defined as nullable arrays/collections of BackedEnums or Objects fail to be properly deserialized during POST or PATCH requests. Instead of being denormalized into instances of the Enum/Entity, the elements are left as raw strings.

This eventually results in database-level errors:
Doctrine\ORM\Mapping\ReflectionEnumProperty::getValue(): Argument #1 ($item) must be of type BackedEnum, string given

The bug is caused by API Platform's AbstractItemNormalizer::createAndValidateAttributeValue which checks if a property's type is a collection of objects using:

if (
    ($t instanceof CollectionType && $collectionValueType instanceof ObjectType)
    || ($t instanceof LegacyType && $t->isCollection() && null !== $collectionValueType && null !== $collectionValueType->getClassName())
)

When a property is nullable, Symfony's TypeInfo extracts a NullableType for its collection values. Since NullableType wraps the actual ObjectType/BackedEnumType but does not directly inherit from ObjectType, the condition $collectionValueType instanceof ObjectType evaluates to false. As a result, API Platform fails to delegate the collection items' denormalization to the Serializer.

How to reproduce

  1. Define a BackedEnum:
namespace App\Enum;

enum ContactNotificationsEnum: string
{
    case Email = 'email';
    case Sms = 'sms';
}
  1. Define a Doctrine entity with a nullable, array of BackedEnums column using Doctrine's SIMPLE_ARRAY (or any typed array PHP property):
namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use App\Enum\ContactNotificationsEnum;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\Types;

#[ORM\Entity]
#[ApiResource]
class CompanyContact
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    public ?int $id = null;

    /**
     * @var ContactNotificationsEnum[] $notificationType
     */
    #[ORM\Column(type: Types::SIMPLE_ARRAY, enumType: ContactNotificationsEnum::class, nullable: true)]
    public array $notificationType = [];
}
  1. Send a POST request to create a new CompanyContact:
{
  "notificationType": ["email"]
}

Results:

A HTTP 500 error is thrown by Doctrine:
Doctrine\ORM\Mapping\ReflectionEnumProperty::getValue(): Argument #1 ($item) must be of type BackedEnum, string given because the array elements remained raw strings.

Additional Context

I guess this behavior only started happening after the transition to Symfony's TypeInfo.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions