Inspired by the awesome tutorial of Ryan Weaver about ApiPlatform on SymfonyCast, I created a Normalizer and a Denormalizer to manage groups normalization and denormalization.
The GroupsDenormalizer
decorates api_platform.serializer.normalizer.item
The GroupsNormalizer
decorates api_platform.jsonld.normalizer.item
I used the #[AsDecorator]
attribute to do it.
All works! My test cover 100% of code. There is no deprecations. As soon as I upgrade from ApiPlatform 3.1.20 to 3.2.0, my code fails and Api is no more available.
TypeError: ApiPlatform\Symfony\Validator\Serializer\ValidationExceptionNormalizer::__construct(): Argument #1 ($decorated) must be of type Symfony\Component\Serializer\Normalizer\NormalizerInterface, App\Security\GroupsDenormalizer given, called in /srv/app/var/cache/test/ContainerCco4qUx/App_KernelTestDebugContainer.php on line 2484
I don't understand why dependency injection sent my denormalizer as argument of ValidationExceptionNormalizer constructor.
I tried to find documentation about migration from 3.1 to 3.2 (without success).
I tried to update the recipe of api-platform.
I tried to change priority.
I tried to define decoration in services.yaml
.
As soon as I delete the GroupsDenormalizer.php
file, Api is available again. (but my code fails because my denormalizer has a job to do :) )
What did I miss? Do you have an idea?
Below, you can find the code of these two classes.
#[AsDecorator(decorates: 'api_platform.jsonld.normalizer.item', priority: 64)]
readonly class GroupsNormalizer implements NormalizerInterface, SerializerAwareInterface
{
public function __construct(
private NormalizerInterface $decorated,
private GroupsGenerator $generator,
) {
}
/**
* @return array<string, bool>
*/
public function getSupportedTypes(?string $format): array
{
return [
Character::class => true,
User::class => true,
];
}
public function normalize(mixed $object, string $format = null, array $context = []): float|int|bool|\ArrayObject|array|string|null
{
if (true /* $object instanceof ResourceInterface */) {
$complement = $this->generator->generateReadingGroups($object);
if (!key_exists('groups', $context)) {
$context['groups'] = [];
}
$context['groups'] = array_merge($context['groups'], $complement);
}
return $this->decorated->normalize($object, $format, $context);
}
public function setSerializer(SerializerInterface $serializer): void
{
if ($this->decorated instanceof SerializerAwareInterface) {
$this->decorated->setSerializer($serializer);
}
}
public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
{
return $this->decorated->supportsNormalization($data, $format);
}
}
#[AsDecorator(decorates: 'api_platform.serializer.normalizer.item', priority: 64)]
readonly class GroupsDenormalizer implements DenormalizerInterface, SerializerAwareInterface
{
public function __construct(
private DenormalizerInterface $decorated,
private GroupsGenerator $generator,
) {
}
public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed
{
if (key_exists('operation', $context) && $context['operation'] instanceof Operation) {
$complement = $this->generator->generateWritingGroups($context['operation'], $type);
if (!key_exists('groups', $context)) {
$context['groups'] = [];
}
$context['groups'] = array_merge($context['groups'], $complement);
}
return $this->decorated->denormalize($data, $type, $format, $context);
}
/**
* @return array<string, bool>
*/
public function getSupportedTypes(?string $format): array
{
return [
Character::class => true,
User::class => true,
];
}
public function setSerializer(SerializerInterface $serializer): void
{
if ($this->decorated instanceof SerializerAwareInterface) {
$this->decorated->setSerializer($serializer);
}
}
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
{
return $this->decorated->supportsDenormalization($data, $type, $format, $context);
}
}
My getSupportedTypes wasn't excluding correctly a lot of classes.
/**
* @return array<string, bool>
*/
public function getSupportedTypes(?string $format): array
{
return [
Character::class => false,
User::class => false,
'*' => null,
];
}