vendor/sylius/resource-bundle/src/Bundle/Controller/ResourceController.php line 156

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Sylius package.
  4.  *
  5.  * (c) Paweł Jędrzejewski
  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 Sylius\Bundle\ResourceBundle\Controller;
  12. use Doctrine\Persistence\ObjectManager;
  13. use FOS\RestBundle\View\View;
  14. use Sylius\Bundle\ResourceBundle\Event\ResourceControllerEvent;
  15. use Sylius\Component\Resource\Exception\DeleteHandlingException;
  16. use Sylius\Component\Resource\Exception\UpdateHandlingException;
  17. use Sylius\Component\Resource\Factory\FactoryInterface;
  18. use Sylius\Component\Resource\Metadata\MetadataInterface;
  19. use Sylius\Component\Resource\Model\ResourceInterface;
  20. use Sylius\Component\Resource\Repository\RepositoryInterface;
  21. use Sylius\Component\Resource\ResourceActions;
  22. use Symfony\Component\DependencyInjection\ContainerAwareTrait;
  23. use Symfony\Component\DependencyInjection\ContainerInterface;
  24. use Symfony\Component\HttpFoundation\Request;
  25. use Symfony\Component\HttpFoundation\Response;
  26. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  27. use Symfony\Component\HttpKernel\Exception\HttpException;
  28. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  29. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  30. class ResourceController
  31. {
  32.     use ControllerTrait;
  33.     use ContainerAwareTrait;
  34.     /** @var MetadataInterface */
  35.     protected $metadata;
  36.     /** @var RequestConfigurationFactoryInterface */
  37.     protected $requestConfigurationFactory;
  38.     /** @var ViewHandlerInterface|null */
  39.     protected $viewHandler;
  40.     /** @var RepositoryInterface */
  41.     protected $repository;
  42.     /** @var FactoryInterface */
  43.     protected $factory;
  44.     /** @var NewResourceFactoryInterface */
  45.     protected $newResourceFactory;
  46.     /** @var ObjectManager */
  47.     protected $manager;
  48.     /** @var SingleResourceProviderInterface */
  49.     protected $singleResourceProvider;
  50.     /** @var ResourcesCollectionProviderInterface */
  51.     protected $resourcesCollectionProvider;
  52.     /** @var ResourceFormFactoryInterface */
  53.     protected $resourceFormFactory;
  54.     /** @var RedirectHandlerInterface */
  55.     protected $redirectHandler;
  56.     /** @var FlashHelperInterface */
  57.     protected $flashHelper;
  58.     /** @var AuthorizationCheckerInterface */
  59.     protected $authorizationChecker;
  60.     /** @var EventDispatcherInterface */
  61.     protected $eventDispatcher;
  62.     /** @var StateMachineInterface|null */
  63.     protected $stateMachine;
  64.     /** @var ResourceUpdateHandlerInterface */
  65.     protected $resourceUpdateHandler;
  66.     /** @var ResourceDeleteHandlerInterface */
  67.     protected $resourceDeleteHandler;
  68.     public function __construct(
  69.         MetadataInterface $metadata,
  70.         RequestConfigurationFactoryInterface $requestConfigurationFactory,
  71.         ?ViewHandlerInterface $viewHandler,
  72.         RepositoryInterface $repository,
  73.         FactoryInterface $factory,
  74.         NewResourceFactoryInterface $newResourceFactory,
  75.         ObjectManager $manager,
  76.         SingleResourceProviderInterface $singleResourceProvider,
  77.         ResourcesCollectionProviderInterface $resourcesFinder,
  78.         ResourceFormFactoryInterface $resourceFormFactory,
  79.         RedirectHandlerInterface $redirectHandler,
  80.         FlashHelperInterface $flashHelper,
  81.         AuthorizationCheckerInterface $authorizationChecker,
  82.         EventDispatcherInterface $eventDispatcher,
  83.         ?StateMachineInterface $stateMachine,
  84.         ResourceUpdateHandlerInterface $resourceUpdateHandler,
  85.         ResourceDeleteHandlerInterface $resourceDeleteHandler
  86.     ) {
  87.         $this->metadata $metadata;
  88.         $this->requestConfigurationFactory $requestConfigurationFactory;
  89.         $this->viewHandler $viewHandler;
  90.         $this->repository $repository;
  91.         $this->factory $factory;
  92.         $this->newResourceFactory $newResourceFactory;
  93.         $this->manager $manager;
  94.         $this->singleResourceProvider $singleResourceProvider;
  95.         $this->resourcesCollectionProvider $resourcesFinder;
  96.         $this->resourceFormFactory $resourceFormFactory;
  97.         $this->redirectHandler $redirectHandler;
  98.         $this->flashHelper $flashHelper;
  99.         $this->authorizationChecker $authorizationChecker;
  100.         $this->eventDispatcher $eventDispatcher;
  101.         $this->stateMachine $stateMachine;
  102.         $this->resourceUpdateHandler $resourceUpdateHandler;
  103.         $this->resourceDeleteHandler $resourceDeleteHandler;
  104.     }
  105.     public function showAction(Request $request): Response
  106.     {
  107.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  108.         $this->isGrantedOr403($configurationResourceActions::SHOW);
  109.         $resource $this->findOr404($configuration);
  110.         $this->eventDispatcher->dispatch(ResourceActions::SHOW$configuration$resource);
  111.         if ($configuration->isHtmlRequest()) {
  112.             return $this->render($configuration->getTemplate(ResourceActions::SHOW '.html'), [
  113.                 'configuration' => $configuration,
  114.                 'metadata' => $this->metadata,
  115.                 'resource' => $resource,
  116.                 $this->metadata->getName() => $resource,
  117.             ]);
  118.         }
  119.         return $this->createRestView($configuration$resource);
  120.     }
  121.     public function indexAction(Request $request): Response
  122.     {
  123.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  124.         $this->isGrantedOr403($configurationResourceActions::INDEX);
  125.         $resources $this->resourcesCollectionProvider->get($configuration$this->repository);
  126.         $this->eventDispatcher->dispatchMultiple(ResourceActions::INDEX$configuration$resources);
  127.         if ($configuration->isHtmlRequest()) {
  128.             return $this->render($configuration->getTemplate(ResourceActions::INDEX '.html'), [
  129.                 'configuration' => $configuration,
  130.                 'metadata' => $this->metadata,
  131.                 'resources' => $resources,
  132.                 $this->metadata->getPluralName() => $resources,
  133.             ]);
  134.         }
  135.         return $this->createRestView($configuration$resources);
  136.     }
  137.     public function createAction(Request $request): Response
  138.     {
  139.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  140.         $this->isGrantedOr403($configurationResourceActions::CREATE);
  141.         $newResource $this->newResourceFactory->create($configuration$this->factory);
  142.         $form $this->resourceFormFactory->create($configuration$newResource);
  143.         $form->handleRequest($request);
  144.         if ($request->isMethod('POST') && $form->isSubmitted() && $form->isValid()) {
  145.             $newResource $form->getData();
  146.             $event $this->eventDispatcher->dispatchPreEvent(ResourceActions::CREATE$configuration$newResource);
  147.             if ($event->isStopped() && !$configuration->isHtmlRequest()) {
  148.                 throw new HttpException($event->getErrorCode(), $event->getMessage());
  149.             }
  150.             if ($event->isStopped()) {
  151.                 $this->flashHelper->addFlashFromEvent($configuration$event);
  152.                 $eventResponse $event->getResponse();
  153.                 if (null !== $eventResponse) {
  154.                     return $eventResponse;
  155.                 }
  156.                 return $this->redirectHandler->redirectToIndex($configuration$newResource);
  157.             }
  158.             if ($configuration->hasStateMachine()) {
  159.                 $stateMachine $this->getStateMachine();
  160.                 $stateMachine->apply($configuration$newResource);
  161.             }
  162.             $this->repository->add($newResource);
  163.             if ($configuration->isHtmlRequest()) {
  164.                 $this->flashHelper->addSuccessFlash($configurationResourceActions::CREATE$newResource);
  165.             }
  166.             $postEvent $this->eventDispatcher->dispatchPostEvent(ResourceActions::CREATE$configuration$newResource);
  167.             if (!$configuration->isHtmlRequest()) {
  168.                 return $this->createRestView($configuration$newResourceResponse::HTTP_CREATED);
  169.             }
  170.             $postEventResponse $postEvent->getResponse();
  171.             if (null !== $postEventResponse) {
  172.                 return $postEventResponse;
  173.             }
  174.             return $this->redirectHandler->redirectToResource($configuration$newResource);
  175.         }
  176.         if (!$configuration->isHtmlRequest()) {
  177.             return $this->createRestView($configuration$formResponse::HTTP_BAD_REQUEST);
  178.         }
  179.         $initializeEvent $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::CREATE$configuration$newResource);
  180.         $initializeEventResponse $initializeEvent->getResponse();
  181.         if (null !== $initializeEventResponse) {
  182.             return $initializeEventResponse;
  183.         }
  184.         return $this->render($configuration->getTemplate(ResourceActions::CREATE '.html'), [
  185.             'configuration' => $configuration,
  186.             'metadata' => $this->metadata,
  187.             'resource' => $newResource,
  188.             $this->metadata->getName() => $newResource,
  189.             'form' => $form->createView(),
  190.         ]);
  191.     }
  192.     public function updateAction(Request $request): Response
  193.     {
  194.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  195.         $this->isGrantedOr403($configurationResourceActions::UPDATE);
  196.         $resource $this->findOr404($configuration);
  197.         $form $this->resourceFormFactory->create($configuration$resource);
  198.         $form->handleRequest($request);
  199.         if (
  200.             in_array($request->getMethod(), ['POST''PUT''PATCH'], true)
  201.             && $form->isSubmitted()
  202.             && $form->isValid()
  203.         ) {
  204.             $resource $form->getData();
  205.             /** @var ResourceControllerEvent $event */
  206.             $event $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE$configuration$resource);
  207.             if ($event->isStopped() && !$configuration->isHtmlRequest()) {
  208.                 throw new HttpException($event->getErrorCode(), $event->getMessage());
  209.             }
  210.             if ($event->isStopped()) {
  211.                 $this->flashHelper->addFlashFromEvent($configuration$event);
  212.                 $eventResponse $event->getResponse();
  213.                 if (null !== $eventResponse) {
  214.                     return $eventResponse;
  215.                 }
  216.                 return $this->redirectHandler->redirectToResource($configuration$resource);
  217.             }
  218.             try {
  219.                 $this->resourceUpdateHandler->handle($resource$configuration$this->manager);
  220.             } catch (UpdateHandlingException $exception) {
  221.                 if (!$configuration->isHtmlRequest()) {
  222.                     return $this->createRestView($configuration$form$exception->getApiResponseCode());
  223.                 }
  224.                 $this->flashHelper->addErrorFlash($configuration$exception->getFlash());
  225.                 return $this->redirectHandler->redirectToReferer($configuration);
  226.             }
  227.             if ($configuration->isHtmlRequest()) {
  228.                 $this->flashHelper->addSuccessFlash($configurationResourceActions::UPDATE$resource);
  229.             }
  230.             $postEvent $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE$configuration$resource);
  231.             if (!$configuration->isHtmlRequest()) {
  232.                 if ($configuration->getParameters()->get('return_content'false)) {
  233.                     return $this->createRestView($configuration$resourceResponse::HTTP_OK);
  234.                 }
  235.                 return $this->createRestView($configurationnullResponse::HTTP_NO_CONTENT);
  236.             }
  237.             $postEventResponse $postEvent->getResponse();
  238.             if (null !== $postEventResponse) {
  239.                 return $postEventResponse;
  240.             }
  241.             return $this->redirectHandler->redirectToResource($configuration$resource);
  242.         }
  243.         if (!$configuration->isHtmlRequest()) {
  244.             return $this->createRestView($configuration$formResponse::HTTP_BAD_REQUEST);
  245.         }
  246.         $initializeEvent $this->eventDispatcher->dispatchInitializeEvent(ResourceActions::UPDATE$configuration$resource);
  247.         $initializeEventResponse $initializeEvent->getResponse();
  248.         if (null !== $initializeEventResponse) {
  249.             return $initializeEventResponse;
  250.         }
  251.         return $this->render($configuration->getTemplate(ResourceActions::UPDATE '.html'), [
  252.             'configuration' => $configuration,
  253.             'metadata' => $this->metadata,
  254.             'resource' => $resource,
  255.             $this->metadata->getName() => $resource,
  256.             'form' => $form->createView(),
  257.         ]);
  258.     }
  259.     public function deleteAction(Request $request): Response
  260.     {
  261.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  262.         $this->isGrantedOr403($configurationResourceActions::DELETE);
  263.         $resource $this->findOr404($configuration);
  264.         if ($configuration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $request->request->get('_csrf_token'))) {
  265.             throw new HttpException(Response::HTTP_FORBIDDEN'Invalid csrf token.');
  266.         }
  267.         $event $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE$configuration$resource);
  268.         if ($event->isStopped() && !$configuration->isHtmlRequest()) {
  269.             throw new HttpException($event->getErrorCode(), $event->getMessage());
  270.         }
  271.         if ($event->isStopped()) {
  272.             $this->flashHelper->addFlashFromEvent($configuration$event);
  273.             $eventResponse $event->getResponse();
  274.             if (null !== $eventResponse) {
  275.                 return $eventResponse;
  276.             }
  277.             return $this->redirectHandler->redirectToIndex($configuration$resource);
  278.         }
  279.         try {
  280.             $this->resourceDeleteHandler->handle($resource$this->repository);
  281.         } catch (DeleteHandlingException $exception) {
  282.             if (!$configuration->isHtmlRequest()) {
  283.                 return $this->createRestView($configurationnull$exception->getApiResponseCode());
  284.             }
  285.             $this->flashHelper->addErrorFlash($configuration$exception->getFlash());
  286.             return $this->redirectHandler->redirectToReferer($configuration);
  287.         }
  288.         if ($configuration->isHtmlRequest()) {
  289.             $this->flashHelper->addSuccessFlash($configurationResourceActions::DELETE$resource);
  290.         }
  291.         $postEvent $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE$configuration$resource);
  292.         if (!$configuration->isHtmlRequest()) {
  293.             return $this->createRestView($configurationnullResponse::HTTP_NO_CONTENT);
  294.         }
  295.         $postEventResponse $postEvent->getResponse();
  296.         if (null !== $postEventResponse) {
  297.             return $postEventResponse;
  298.         }
  299.         return $this->redirectHandler->redirectToIndex($configuration$resource);
  300.     }
  301.     public function bulkDeleteAction(Request $request): Response
  302.     {
  303.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  304.         $this->isGrantedOr403($configurationResourceActions::BULK_DELETE);
  305.         $resources $this->resourcesCollectionProvider->get($configuration$this->repository);
  306.         if (
  307.             $configuration->isCsrfProtectionEnabled() &&
  308.             !$this->isCsrfTokenValid(ResourceActions::BULK_DELETE$request->request->get('_csrf_token'))
  309.         ) {
  310.             throw new HttpException(Response::HTTP_FORBIDDEN'Invalid csrf token.');
  311.         }
  312.         $this->eventDispatcher->dispatchMultiple(ResourceActions::BULK_DELETE$configuration$resources);
  313.         foreach ($resources as $resource) {
  314.             $event $this->eventDispatcher->dispatchPreEvent(ResourceActions::DELETE$configuration$resource);
  315.             if ($event->isStopped() && !$configuration->isHtmlRequest()) {
  316.                 throw new HttpException($event->getErrorCode(), $event->getMessage());
  317.             }
  318.             if ($event->isStopped()) {
  319.                 $this->flashHelper->addFlashFromEvent($configuration$event);
  320.                 $eventResponse $event->getResponse();
  321.                 if (null !== $eventResponse) {
  322.                     return $eventResponse;
  323.                 }
  324.                 return $this->redirectHandler->redirectToIndex($configuration$resource);
  325.             }
  326.             try {
  327.                 $this->resourceDeleteHandler->handle($resource$this->repository);
  328.             } catch (DeleteHandlingException $exception) {
  329.                 if (!$configuration->isHtmlRequest()) {
  330.                     return $this->createRestView($configurationnull$exception->getApiResponseCode());
  331.                 }
  332.                 $this->flashHelper->addErrorFlash($configuration$exception->getFlash());
  333.                 return $this->redirectHandler->redirectToReferer($configuration);
  334.             }
  335.             $postEvent $this->eventDispatcher->dispatchPostEvent(ResourceActions::DELETE$configuration$resource);
  336.         }
  337.         if (!$configuration->isHtmlRequest()) {
  338.             return $this->createRestView($configurationnullResponse::HTTP_NO_CONTENT);
  339.         }
  340.         $this->flashHelper->addSuccessFlash($configurationResourceActions::BULK_DELETE);
  341.         if (isset($postEvent)) {
  342.             $postEventResponse $postEvent->getResponse();
  343.             if (null !== $postEventResponse) {
  344.                 return $postEventResponse;
  345.             }
  346.         }
  347.         return $this->redirectHandler->redirectToIndex($configuration);
  348.     }
  349.     public function applyStateMachineTransitionAction(Request $request): Response
  350.     {
  351.         $stateMachine $this->getStateMachine();
  352.         $configuration $this->requestConfigurationFactory->create($this->metadata$request);
  353.         $this->isGrantedOr403($configurationResourceActions::UPDATE);
  354.         $resource $this->findOr404($configuration);
  355.         if ($configuration->isCsrfProtectionEnabled() && !$this->isCsrfTokenValid((string) $resource->getId(), $request->get('_csrf_token'))) {
  356.             throw new HttpException(Response::HTTP_FORBIDDEN'Invalid CSRF token.');
  357.         }
  358.         $event $this->eventDispatcher->dispatchPreEvent(ResourceActions::UPDATE$configuration$resource);
  359.         if ($event->isStopped() && !$configuration->isHtmlRequest()) {
  360.             throw new HttpException($event->getErrorCode(), $event->getMessage());
  361.         }
  362.         if ($event->isStopped()) {
  363.             $this->flashHelper->addFlashFromEvent($configuration$event);
  364.             $eventResponse $event->getResponse();
  365.             if (null !== $eventResponse) {
  366.                 return $eventResponse;
  367.             }
  368.             return $this->redirectHandler->redirectToResource($configuration$resource);
  369.         }
  370.         if (!$stateMachine->can($configuration$resource)) {
  371.             throw new BadRequestHttpException();
  372.         }
  373.         try {
  374.             $this->resourceUpdateHandler->handle($resource$configuration$this->manager);
  375.         } catch (UpdateHandlingException $exception) {
  376.             if (!$configuration->isHtmlRequest()) {
  377.                 return $this->createRestView($configuration$resource$exception->getApiResponseCode());
  378.             }
  379.             $this->flashHelper->addErrorFlash($configuration$exception->getFlash());
  380.             return $this->redirectHandler->redirectToReferer($configuration);
  381.         }
  382.         if ($configuration->isHtmlRequest()) {
  383.             $this->flashHelper->addSuccessFlash($configurationResourceActions::UPDATE$resource);
  384.         }
  385.         $postEvent $this->eventDispatcher->dispatchPostEvent(ResourceActions::UPDATE$configuration$resource);
  386.         if (!$configuration->isHtmlRequest()) {
  387.             if ($configuration->getParameters()->get('return_content'true)) {
  388.                 return $this->createRestView($configuration$resourceResponse::HTTP_OK);
  389.             }
  390.             return $this->createRestView($configurationnullResponse::HTTP_NO_CONTENT);
  391.         }
  392.         $postEventResponse $postEvent->getResponse();
  393.         if (null !== $postEventResponse) {
  394.             return $postEventResponse;
  395.         }
  396.         return $this->redirectHandler->redirectToResource($configuration$resource);
  397.     }
  398.     /**
  399.      * @return mixed
  400.      */
  401.     protected function getParameter(string $name)
  402.     {
  403.         if (!$this->container instanceof ContainerInterface) {
  404.             throw new \RuntimeException(sprintf(
  405.                 'Container passed to "%s" has to implements "%s".',
  406.                 self::class,
  407.                 ContainerInterface::class
  408.             ));
  409.         }
  410.         return $this->container->getParameter($name);
  411.     }
  412.     /**
  413.      * @throws AccessDeniedException
  414.      */
  415.     protected function isGrantedOr403(RequestConfiguration $configurationstring $permission): void
  416.     {
  417.         if (!$configuration->hasPermission()) {
  418.             return;
  419.         }
  420.         $permission $configuration->getPermission($permission);
  421.         if (!$this->authorizationChecker->isGranted($configuration$permission)) {
  422.             throw new AccessDeniedException();
  423.         }
  424.     }
  425.     /**
  426.      * @throws NotFoundHttpException
  427.      */
  428.     protected function findOr404(RequestConfiguration $configuration): ResourceInterface
  429.     {
  430.         if (null === $resource $this->singleResourceProvider->get($configuration$this->repository)) {
  431.             throw new NotFoundHttpException(sprintf('The "%s" has not been found'$this->metadata->getHumanizedName()));
  432.         }
  433.         return $resource;
  434.     }
  435.     /**
  436.      * @param mixed $data
  437.      */
  438.     protected function createRestView(RequestConfiguration $configuration$dataint $statusCode null): Response
  439.     {
  440.         if (null === $this->viewHandler) {
  441.             throw new \LogicException('You can not use the "non-html" request if FriendsOfSymfony Rest Bundle is not available. Try running "composer require friendsofsymfony/rest-bundle".');
  442.         }
  443.         $view View::create($data$statusCode);
  444.         return $this->viewHandler->handle($configuration$view);
  445.     }
  446.     protected function getStateMachine(): StateMachineInterface
  447.     {
  448.         if (null === $this->stateMachine) {
  449.             throw new \LogicException('You can not use the "state-machine" if Winzou State Machine Bundle is not available. Try running "composer require winzou/state-machine-bundle".');
  450.         }
  451.         return $this->stateMachine;
  452.     }
  453. }