vendor/friendsofsymfony/rest-bundle/View/ViewHandler.php line 158

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the FOSRestBundle package.
  4.  *
  5.  * (c) FriendsOfSymfony <http://friendsofsymfony.github.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. namespace FOS\RestBundle\View;
  11. use FOS\RestBundle\Context\Context;
  12. use FOS\RestBundle\Serializer\Serializer;
  13. use Symfony\Component\Form\FormInterface;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\HttpFoundation\RequestStack;
  16. use Symfony\Component\HttpFoundation\Response;
  17. use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
  18. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  19. /**
  20.  * View may be used in controllers to build up a response in a format agnostic way
  21.  * The View class takes care of encoding your data in json, xml via the Serializer
  22.  * component.
  23.  *
  24.  * @author Jordi Boggiano <j.boggiano@seld.be>
  25.  * @author Lukas K. Smith <smith@pooteeweet.org>
  26.  */
  27. final class ViewHandler implements ConfigurableViewHandlerInterface
  28. {
  29.     /**
  30.      * Key format, value a callable that returns a Response instance.
  31.      *
  32.      * @var array
  33.      */
  34.     private $customHandlers = [];
  35.     /**
  36.      * The supported formats as keys.
  37.      *
  38.      * @var array
  39.      */
  40.     private $formats;
  41.     private $failedValidationCode;
  42.     private $emptyContentCode;
  43.     private $serializeNull;
  44.     private $exclusionStrategyGroups = [];
  45.     private $exclusionStrategyVersion;
  46.     private $serializeNullStrategy;
  47.     private $urlGenerator;
  48.     private $serializer;
  49.     private $requestStack;
  50.     private $options;
  51.     private function __construct(
  52.         UrlGeneratorInterface $urlGenerator,
  53.         Serializer $serializer,
  54.         RequestStack $requestStack,
  55.         array $formats null,
  56.         int $failedValidationCode Response::HTTP_BAD_REQUEST,
  57.         int $emptyContentCode Response::HTTP_NO_CONTENT,
  58.         bool $serializeNull false,
  59.         array $options = []
  60.     ) {
  61.         $this->urlGenerator $urlGenerator;
  62.         $this->serializer $serializer;
  63.         $this->requestStack $requestStack;
  64.         $this->formats = (array) $formats;
  65.         $this->failedValidationCode $failedValidationCode;
  66.         $this->emptyContentCode $emptyContentCode;
  67.         $this->serializeNull $serializeNull;
  68.         $this->options $options + [
  69.             'exclusionStrategyGroups' => [],
  70.             'exclusionStrategyVersion' => null,
  71.             'serializeNullStrategy' => null,
  72.             ];
  73.         $this->reset();
  74.     }
  75.     public static function create(
  76.         UrlGeneratorInterface $urlGenerator,
  77.         Serializer $serializer,
  78.         RequestStack $requestStack,
  79.         array $formats null,
  80.         int $failedValidationCode Response::HTTP_BAD_REQUEST,
  81.         int $emptyContentCode Response::HTTP_NO_CONTENT,
  82.         bool $serializeNull false,
  83.         array $options = []
  84.     ): self {
  85.         return new self($urlGenerator$serializer$requestStack$formats$failedValidationCode$emptyContentCode$serializeNull$options);
  86.     }
  87.     /**
  88.      * @param string[]|string $groups
  89.      */
  90.     public function setExclusionStrategyGroups($groups): void
  91.     {
  92.         $this->exclusionStrategyGroups = (array) $groups;
  93.     }
  94.     public function setExclusionStrategyVersion(string $version): void
  95.     {
  96.         $this->exclusionStrategyVersion $version;
  97.     }
  98.     public function setSerializeNullStrategy(bool $isEnabled): void
  99.     {
  100.         $this->serializeNullStrategy $isEnabled;
  101.     }
  102.     /**
  103.      * {@inheritdoc}
  104.      */
  105.     public function supports(string $format): bool
  106.     {
  107.         return isset($this->customHandlers[$format]) || isset($this->formats[$format]);
  108.     }
  109.     /**
  110.      * Registers a custom handler.
  111.      *
  112.      * The handler must have the following signature: handler(ViewHandler $viewHandler, View $view, Request $request, $format)
  113.      * It can use the public methods of this class to retrieve the needed data and return a
  114.      * Response object ready to be sent.
  115.      */
  116.     public function registerHandler(string $format, callable $callable): void
  117.     {
  118.         $this->customHandlers[$format] = $callable;
  119.     }
  120.     /**
  121.      * Handles a request with the proper handler.
  122.      *
  123.      * Decides on which handler to use based on the request format.
  124.      *
  125.      * @throws UnsupportedMediaTypeHttpException
  126.      */
  127.     public function handle(View $viewRequest $request null): Response
  128.     {
  129.         if (null === $request) {
  130.             $request $this->requestStack->getCurrentRequest();
  131.         }
  132.         $format $view->getFormat() ?: $request->getRequestFormat();
  133.         if (!$this->supports($format)) {
  134.             $msg "Format '$format' not supported, handler must be implemented";
  135.             throw new UnsupportedMediaTypeHttpException($msg);
  136.         }
  137.         if (isset($this->customHandlers[$format])) {
  138.             return call_user_func($this->customHandlers[$format], $this$view$request$format);
  139.         }
  140.         return $this->createResponse($view$request$format);
  141.     }
  142.     public function createRedirectResponse(View $viewstring $locationstring $format): Response
  143.     {
  144.         $content null;
  145.         if ((Response::HTTP_CREATED === $view->getStatusCode() || Response::HTTP_ACCEPTED === $view->getStatusCode()) && null !== $view->getData()) {
  146.             $response $this->initResponse($view$format);
  147.         } else {
  148.             $response $view->getResponse();
  149.         }
  150.         $code $this->getStatusCode($view$content);
  151.         $response->setStatusCode($code);
  152.         $response->headers->set('Location'$location);
  153.         return $response;
  154.     }
  155.     public function createResponse(View $viewRequest $requeststring $format): Response
  156.     {
  157.         $route $view->getRoute();
  158.         $location $route
  159.             $this->urlGenerator->generate($route, (array) $view->getRouteParameters(), UrlGeneratorInterface::ABSOLUTE_URL)
  160.             : $view->getLocation();
  161.         if ($location) {
  162.             return $this->createRedirectResponse($view$location$format);
  163.         }
  164.         $response $this->initResponse($view$format);
  165.         if (!$response->headers->has('Content-Type')) {
  166.             $mimeType $request->attributes->get('media_type');
  167.             if (null === $mimeType) {
  168.                 $mimeType $request->getMimeType($format);
  169.             }
  170.             $response->headers->set('Content-Type'$mimeType);
  171.         }
  172.         return $response;
  173.     }
  174.     /**
  175.      * Gets a response HTTP status code from a View instance.
  176.      *
  177.      * By default it will return 200. However if there is a FormInterface stored for
  178.      * the key 'form' in the View's data it will return the failed_validation
  179.      * configuration if the form instance has errors.
  180.      *
  181.      * @param string|false|null
  182.      */
  183.     private function getStatusCode(View $view$content null): int
  184.     {
  185.         $form $this->getFormFromView($view);
  186.         if (null !== $form && $form->isSubmitted() && !$form->isValid()) {
  187.             return $this->failedValidationCode;
  188.         }
  189.         $statusCode $view->getStatusCode();
  190.         if (null !== $statusCode) {
  191.             return $statusCode;
  192.         }
  193.         return null !== $content Response::HTTP_OK $this->emptyContentCode;
  194.     }
  195.     private function getSerializationContext(View $view): Context
  196.     {
  197.         $context $view->getContext();
  198.         $groups $context->getGroups();
  199.         if (empty($groups) && $this->exclusionStrategyGroups) {
  200.             $context->setGroups($this->exclusionStrategyGroups);
  201.         }
  202.         if (null === $context->getVersion() && $this->exclusionStrategyVersion) {
  203.             $context->setVersion($this->exclusionStrategyVersion);
  204.         }
  205.         if (null === $context->getSerializeNull() && null !== $this->serializeNullStrategy) {
  206.             $context->setSerializeNull($this->serializeNullStrategy);
  207.         }
  208.         if (null !== $view->getStatusCode() && !$context->hasAttribute('status_code')) {
  209.             $context->setAttribute('status_code'$view->getStatusCode());
  210.         }
  211.         return $context;
  212.     }
  213.     private function initResponse(View $viewstring $format): Response
  214.     {
  215.         $content null;
  216.         if ($this->serializeNull || null !== $view->getData()) {
  217.             $data $this->getDataFromView($view);
  218.             if ($data instanceof FormInterface && $data->isSubmitted() && !$data->isValid()) {
  219.                 $view->getContext()->setAttribute('status_code'$this->failedValidationCode);
  220.             }
  221.             $context $this->getSerializationContext($view);
  222.             $content $this->serializer->serialize($data$format$context);
  223.         }
  224.         $response $view->getResponse();
  225.         $response->setStatusCode($this->getStatusCode($view$content));
  226.         if (null !== $content) {
  227.             $response->setContent($content);
  228.         }
  229.         return $response;
  230.     }
  231.     private function getFormFromView(View $view): ?FormInterface
  232.     {
  233.         $data $view->getData();
  234.         if ($data instanceof FormInterface) {
  235.             return $data;
  236.         }
  237.         if (is_array($data) && isset($data['form']) && $data['form'] instanceof FormInterface) {
  238.             return $data['form'];
  239.         }
  240.         return null;
  241.     }
  242.     private function getDataFromView(View $view)
  243.     {
  244.         $form $this->getFormFromView($view);
  245.         if (null === $form) {
  246.             return $view->getData();
  247.         }
  248.         return $form;
  249.     }
  250.     public function reset(): void
  251.     {
  252.         $this->exclusionStrategyGroups $this->options['exclusionStrategyGroups'];
  253.         $this->exclusionStrategyVersion $this->options['exclusionStrategyVersion'];
  254.         $this->serializeNullStrategy $this->options['serializeNullStrategy'];
  255.     }
  256. }