phpsymfonydeserializationsymfony-serializer

How to pass context to Symfony Serializer?


I use Symfony Serializer component outside the framework. I have custom normalizer and I want to pass some extra context to it from the field like this:

class TariffDto
{
    #[Context([EntityNormalizer::UNIQUE_ENTITY_FIELD => 'code'])]
    public Product $product;
}

The EntityNormalizer doesn't extend any normalizer, it just implements DenormalizerInterface (I don't need NormalizerInterface in this case):

class EntityNormalizer implements DenormalizerInterface
{
    public const UNIQUE_ENTITY_FIELD = 'unique_entity_field';

    private EntityManager $em;

    public function __construct()
    {
        $this->em = Registry::getInstance()->EM;
    }

    public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
    {
        return str_starts_with($type, 'App\\Entity\\') && (is_numeric($data) || is_string($data));
    }

    public function denormalize(mixed $data, string $type, ?string $format = null, array $context = [])
    {
        dump($context);
        return $this->em->getRepository($type)->find($data);
    }
}

And dump($context) prints this result:

^ array:3 [
  "_read_attributes" => false
  "cache_key" => "45e90c83367464cde5c29c1ac70e95f3-product"
  "deserialization_path" => "product"
]

I use this serializer configuration:

$phpDocExtractor = new PropertyInfo\Extractor\PhpDocExtractor();
$typeExtractor   = new PropertyInfo\PropertyInfoExtractor(
    typeExtractors: [$phpDocExtractor, new ReflectionExtractor()]
);
$serializer = new Serializer\Serializer(
    normalizers: [
        new BackedEnumNormalizer(),
        new EntityNormalizer(), // my custom normalizer
        new Serializer\Normalizer\ObjectNormalizer(
            nameConverter: new Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter(),
            propertyTypeExtractor: $typeExtractor
        ),

        new Serializer\Normalizer\ArrayDenormalizer(),
    ],
);

What I'm doing wrong, what I've missed?
I've tried to pass different context keys, incl. the existing one (#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]), but nothing changed, I still have no extra context in denormalize method.

I've found this article, and here it described exactly as I want, and I guess it worked for that guy.

So, where's my context?


Solution

  • You will also need to initialise the ClassMetadataFactory and the AnnotationLoader to process the attributes.

    If you use the serializer standalone, you have to configure everything yourself, including the processing of the attributes. If you don't use the standalone serializer, Symfony will do this for you.

    $phpDocExtractor = new PhpDocExtractor();
    $typeExtractor   = new PropertyInfoExtractor(
        typeExtractors: [$phpDocExtractor, new ReflectionExtractor()]
    );
    $serializer = new Serializer(
        normalizers: [
            new BackedEnumNormalizer(),
            new EntityNormalizer(),
            new ObjectNormalizer(
                classMetadataFactory: new ClassMetadataFactory(new AnnotationLoader()),
                nameConverter: new CamelCaseToSnakeCaseNameConverter(),
                propertyTypeExtractor: $typeExtractor
            ),
            new ArrayDenormalizer()
        ],
    );
    

    I put your classes in a test and checked it.

    Kind regards from this guy. ;-)