In API Platform, I am trying to decorate the service api_platform.jsonld.normalizer.item
(class ApiPlatform\JsonLd\Serializer\ItemNormalizer
).
I notice this is causing some strange behavior in my application. For example, I get an error Specified class App\Dto\File\FileMultipleResponseDto is not a resource class.
which is not showing before setting up the decorator.
Here is my decorator :
<?php
declare(strict_types=1);
namespace App\Serializer;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
#[AsDecorator('api_platform.jsonld.normalizer.item')]
class ItemJsonLdNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface
{
public function __construct(
private readonly NormalizerInterface $normalizer,
) {}
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
return $this->normalizer->normalize($object, $format, $context);
}
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
{
return $this->normalizer->supportsDenormalization($data, $type, $format, $context);
}
public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed
{
return $this->normalizer->denormalize($data, $type, $format, $context);
}
public function hasCacheableSupportsMethod(): bool
{
return $this->normalizer->hasCacheableSupportsMethod();
}
public function supportsNormalization(mixed $data, string $format = null)
{
return $this->normalizer->supportsNormalization($data, $format);
}
public function setSerializer(SerializerInterface $serializer)
{
return $this->normalizer->setSerializer($serializer);
}
}
As you can see, it's currently doing nothing appart from decorating the original service. What am I doing wrong ?
So, after some debugging (thanks to the bin/console debug:container normalizer
command) I found out the issue : it seems that API Platform ElasticSearch component was already decorating the JsonLD ItemNormalizer (ApiPlatform\Elasticsearch\Serializer\ItemNormalizer
), with a higher priority (-895). It causes some issues in the application which I did not investigate further.
I used a higher priority than the API Platform decorator :
<?php
declare(strict_types=1);
namespace App\Serializer;
use ApiPlatform\Metadata\Post;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated;
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\SerializerAwareInterface;
use Symfony\Component\Serializer\SerializerInterface;
#[AsDecorator('api_platform.jsonld.normalizer.item', priority: -900)]
class ItemJsonLdNormalizer implements NormalizerInterface, DenormalizerInterface, SerializerAwareInterface, CacheableSupportsMethodInterface
{
public function __construct(
#[AutowireDecorated] private readonly NormalizerInterface $decorated,
) {}
public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
{
return $this->decorated->normalize($object, $format, $context);
}
public function supportsDenormalization(mixed $data, string $type, string $format = null, array $context = []): bool
{
return $this->decorated->supportsDenormalization($data, $type, $format, $context);
}
public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed
{
// When denormalizing some API Platform resource,
// we want to allow clients to send the resource identifier in POST operations.
// By default, it's only allowed on PUT/PATCH operations.
if ($context['operation'] instanceof Post) {
$context['api_allow_update'] = true;
}
return $this->decorated->denormalize($data, $type, $format, $context);
}
public function hasCacheableSupportsMethod(): bool
{
return $this->decorated->hasCacheableSupportsMethod();
}
public function supportsNormalization(mixed $data, string $format = null)
{
return $this->decorated->supportsNormalization($data, $format);
}
public function setSerializer(SerializerInterface $serializer)
{
return $this->decorated->setSerializer($serializer);
}
}
My normalizer is now properly taken into consideration for JsonLD requests. Be careful, you might have to implement a similar normalizer for Json requests as well, because they won't go into this created normalizer.
For the reference, I was looking to fix this issue in my code : https://github.com/api-platform/api-platform/issues/343