vendor/api-platform/core/src/Symfony/EventListener/SerializeListener.php line 51

  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <dunglas@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Symfony\EventListener;
  12. use ApiPlatform\Doctrine\Orm\State\Options;
  13. use ApiPlatform\Exception\RuntimeException;
  14. use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
  15. use ApiPlatform\Serializer\ResourceList;
  16. use ApiPlatform\Serializer\SerializerContextBuilderInterface;
  17. use ApiPlatform\Util\OperationRequestInitiatorTrait;
  18. use ApiPlatform\Util\RequestAttributesExtractor;
  19. use Symfony\Component\HttpFoundation\Request;
  20. use Symfony\Component\HttpFoundation\Response;
  21. use Symfony\Component\HttpKernel\Event\ViewEvent;
  22. use Symfony\Component\Serializer\Encoder\EncoderInterface;
  23. use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
  24. use Symfony\Component\Serializer\SerializerInterface;
  25. use Symfony\Component\WebLink\GenericLinkProvider;
  26. use Symfony\Component\WebLink\Link;
  27. /**
  28.  * Serializes data.
  29.  *
  30.  * @author Kévin Dunglas <dunglas@gmail.com>
  31.  */
  32. final class SerializeListener
  33. {
  34.     use OperationRequestInitiatorTrait;
  35.     public const OPERATION_ATTRIBUTE_KEY 'serialize';
  36.     public function __construct(private readonly SerializerInterface $serializer, private readonly SerializerContextBuilderInterface $serializerContextBuilder, ?ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory null)
  37.     {
  38.         $this->resourceMetadataCollectionFactory $resourceMetadataFactory;
  39.     }
  40.     /**
  41.      * Serializes the data to the requested format.
  42.      */
  43.     public function onKernelView(ViewEvent $event): void
  44.     {
  45.         $controllerResult $event->getControllerResult();
  46.         $request $event->getRequest();
  47.         if ($controllerResult instanceof Response) {
  48.             return;
  49.         }
  50.         $attributes RequestAttributesExtractor::extractAttributes($request);
  51.         if (!($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond'false))) {
  52.             return;
  53.         }
  54.         $operation $this->initializeOperation($request);
  55.         if (!($operation?->canSerialize() ?? true)) {
  56.             return;
  57.         }
  58.         if (!$attributes) {
  59.             $this->serializeRawData($event$request$controllerResult);
  60.             return;
  61.         }
  62.         $context $this->serializerContextBuilder->createFromRequest($requesttrue$attributes);
  63.         if (isset($context['output']) && \array_key_exists('class'$context['output']) && null === $context['output']['class']) {
  64.             $event->setControllerResult(null);
  65.             return;
  66.         }
  67.         if ($included $request->attributes->get('_api_included')) {
  68.             $context['api_included'] = $included;
  69.         }
  70.         $resources = new ResourceList();
  71.         $context['resources'] = &$resources;
  72.         $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'resources';
  73.         $resourcesToPush = new ResourceList();
  74.         $context['resources_to_push'] = &$resourcesToPush;
  75.         $context[AbstractObjectNormalizer::EXCLUDE_FROM_CACHE_KEY][] = 'resources_to_push';
  76.         if (($options $operation?->getStateOptions()) && $options instanceof Options && $options->getEntityClass()) {
  77.             $context['force_resource_class'] = $operation->getClass();
  78.         }
  79.         $request->attributes->set('_api_normalization_context'$context);
  80.         $event->setControllerResult($this->serializer->serialize($controllerResult$request->getRequestFormat(), $context));
  81.         $request->attributes->set('_resources'$request->attributes->get('_resources', []) + (array) $resources);
  82.         if (!\count($resourcesToPush)) {
  83.             return;
  84.         }
  85.         $linkProvider $request->attributes->get('_links', new GenericLinkProvider());
  86.         foreach ($resourcesToPush as $resourceToPush) {
  87.             $linkProvider $linkProvider->withLink((new Link('preload'$resourceToPush))->withAttribute('as''fetch'));
  88.         }
  89.         $request->attributes->set('_links'$linkProvider);
  90.     }
  91.     /**
  92.      * Tries to serialize data that are not API resources (e.g. the entrypoint or data returned by a custom controller).
  93.      *
  94.      * @throws RuntimeException
  95.      */
  96.     private function serializeRawData(ViewEvent $eventRequest $request$controllerResult): void
  97.     {
  98.         if (\is_object($controllerResult)) {
  99.             $event->setControllerResult($this->serializer->serialize($controllerResult$request->getRequestFormat(), $request->attributes->get('_api_normalization_context', [])));
  100.             return;
  101.         }
  102.         if (!$this->serializer instanceof EncoderInterface) {
  103.             throw new RuntimeException(sprintf('The serializer must implement the "%s" interface.'EncoderInterface::class));
  104.         }
  105.         $event->setControllerResult($this->serializer->encode($controllerResult$request->getRequestFormat()));
  106.     }
  107. }