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

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