vendor/shopware/storefront/Controller/CmsController.php line 57

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Content\Category\SalesChannel\AbstractCategoryRoute;
  4. use Shopware\Core\Content\Cms\Exception\PageNotFoundException;
  5. use Shopware\Core\Content\Cms\SalesChannel\AbstractCmsRoute;
  6. use Shopware\Core\Content\Product\SalesChannel\Detail\AbstractProductDetailRoute;
  7. use Shopware\Core\Content\Product\SalesChannel\FindVariant\AbstractFindProductVariantRoute;
  8. use Shopware\Core\Content\Product\SalesChannel\Listing\AbstractProductListingRoute;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  10. use Shopware\Core\Framework\Log\Package;
  11. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  12. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  13. use Shopware\Storefront\Event\SwitchBuyBoxVariantEvent;
  14. use Shopware\Storefront\Page\Cms\CmsPageLoadedHook;
  15. use Shopware\Storefront\Page\Product\Review\ProductReviewLoader;
  16. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  17. use Symfony\Component\HttpFoundation\JsonResponse;
  18. use Symfony\Component\HttpFoundation\Request;
  19. use Symfony\Component\HttpFoundation\Response;
  20. use Symfony\Component\Routing\Annotation\Route;
  21. /**
  22.  * @internal
  23.  * Do not use direct or indirect repository calls in a controller. Always use a store-api route to get or put data
  24.  */
  25. #[Route(defaults: ['_routeScope' => ['storefront']])]
  26. #[Package('content')]
  27. class CmsController extends StorefrontController
  28. {
  29.     /**
  30.      * @internal
  31.      */
  32.     public function __construct(
  33.         private readonly AbstractCmsRoute $cmsRoute,
  34.         private readonly AbstractCategoryRoute $categoryRoute,
  35.         private readonly AbstractProductListingRoute $listingRoute,
  36.         private readonly AbstractProductDetailRoute $productRoute,
  37.         private readonly ProductReviewLoader $productReviewLoader,
  38.         private readonly AbstractFindProductVariantRoute $findVariantRoute,
  39.         private readonly EventDispatcherInterface $eventDispatcher
  40.     ) {
  41.     }
  42.     #[Route(path'/widgets/cms/{id}'name'frontend.cms.page'defaults: ['id' => null'XmlHttpRequest' => true'_httpCache' => true], methods: ['GET''POST'])]
  43.     public function page(?string $idRequest $requestSalesChannelContext $salesChannelContext): Response
  44.     {
  45.         if (!$id) {
  46.             throw new MissingRequestParameterException('id');
  47.         }
  48.         $page $this->cmsRoute->load($id$request$salesChannelContext)->getCmsPage();
  49.         $this->hook(new CmsPageLoadedHook($page$salesChannelContext));
  50.         $response $this->renderStorefront('@Storefront/storefront/page/content/detail.html.twig', ['cmsPage' => $page]);
  51.         $response->headers->set('x-robots-tag''noindex');
  52.         return $response;
  53.     }
  54.     /**
  55.      * Navigation id is required to load the slot config for the navigation
  56.      */
  57.     #[Route(path'/widgets/cms/navigation/{navigationId}'name'frontend.cms.navigation.page'defaults: ['navigationId' => null'XmlHttpRequest' => true], methods: ['GET''POST'])]
  58.     public function category(?string $navigationIdRequest $requestSalesChannelContext $salesChannelContext): Response
  59.     {
  60.         if (!$navigationId) {
  61.             throw new MissingRequestParameterException('navigationId');
  62.         }
  63.         $category $this->categoryRoute->load($navigationId$request$salesChannelContext)->getCategory();
  64.         $page $category->getCmsPage();
  65.         if (!$page) {
  66.             throw new PageNotFoundException('');
  67.         }
  68.         $this->hook(new CmsPageLoadedHook($page$salesChannelContext));
  69.         $response $this->renderStorefront('@Storefront/storefront/page/content/detail.html.twig', ['cmsPage' => $page]);
  70.         $response->headers->set('x-robots-tag''noindex');
  71.         return $response;
  72.     }
  73.     /**
  74.      * Route to load the listing filters
  75.      */
  76.     #[Route(path'/widgets/cms/navigation/{navigationId}/filter'name'frontend.cms.navigation.filter'defaults: ['XmlHttpRequest' => true'_routeScope' => ['storefront'], '_httpCache' => true], methods: ['GET''POST'])]
  77.     public function filter(string $navigationIdRequest $requestSalesChannelContext $context): Response
  78.     {
  79.         // Allows to fetch only aggregations over the gateway.
  80.         $request->request->set('only-aggregations'true);
  81.         // Allows to convert all post-filters to filters. This leads to the fact that only aggregation values are returned, which are combinable with the previous applied filters.
  82.         $request->request->set('reduce-aggregations'true);
  83.         $listing $this->listingRoute
  84.             ->load($navigationId$request$context, new Criteria())
  85.             ->getResult();
  86.         $mapped = [];
  87.         foreach ($listing->getAggregations() as $aggregation) {
  88.             $mapped[$aggregation->getName()] = $aggregation;
  89.         }
  90.         $response = new JsonResponse($mapped);
  91.         $response->headers->set('x-robots-tag''noindex');
  92.         return $response;
  93.     }
  94.     /**
  95.      * Route to load the cms element buy box product config which assigned to the provided product id.
  96.      * Product id is required to load the slot config for the buy box
  97.      */
  98.     #[Route(path'/widgets/cms/buybox/{productId}/switch'name'frontend.cms.buybox.switch'defaults: ['productId' => null'XmlHttpRequest' => true'_routeScope' => ['storefront'], '_httpCache' => true], methods: ['GET'])]
  99.     public function switchBuyBoxVariant(string $productIdRequest $requestSalesChannelContext $context): Response
  100.     {
  101.         /** @var string $elementId */
  102.         $elementId $request->query->get('elementId');
  103.         /** @var string[]|null $options */
  104.         $options json_decode($request->query->get('options'''), true);
  105.         $variantResponse $this->findVariantRoute->load(
  106.             $productId,
  107.             new Request(
  108.                 [
  109.                     'switchedGroup' => $request->query->get('switched'),
  110.                     'options' => $options ?? [],
  111.                 ]
  112.             ),
  113.             $context
  114.         );
  115.         $newProductId $variantResponse->getFoundCombination()->getVariantId();
  116.         $result $this->productRoute->load($newProductId$request$context, new Criteria());
  117.         $product $result->getProduct();
  118.         $configurator $result->getConfigurator();
  119.         $request->request->set('parentId'$product->getParentId());
  120.         $request->request->set('productId'$product->getId());
  121.         $reviews $this->productReviewLoader->load($request$context);
  122.         $reviews->setParentId($product->getParentId() ?? $product->getId());
  123.         $event = new SwitchBuyBoxVariantEvent($elementId$product$configurator$request$context);
  124.         $this->eventDispatcher->dispatch($event);
  125.         $response $this->renderStorefront('@Storefront/storefront/component/buy-widget/buy-widget.html.twig', [
  126.             'product' => $product,
  127.             'configuratorSettings' => $configurator,
  128.             'totalReviews' => $reviews->getTotalReviews(),
  129.             'elementId' => $elementId,
  130.         ]);
  131.         $response->headers->set('x-robots-tag''noindex');
  132.         return $response;
  133.     }
  134. }