vendor/shopware/platform/src/Storefront/Controller/ProductController.php line 91

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Content\Product\Exception\ProductNotFoundException;
  4. use Shopware\Core\Content\Product\Exception\ReviewNotActiveExeption;
  5. use Shopware\Core\Content\Product\SalesChannel\Review\AbstractProductReviewSaveRoute;
  6. use Shopware\Core\Content\Seo\SeoUrlPlaceholderHandlerInterface;
  7. use Shopware\Core\Framework\Routing\Annotation\LoginRequired;
  8. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  9. use Shopware\Core\Framework\Routing\Annotation\Since;
  10. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  11. use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
  12. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  13. use Shopware\Core\System\SystemConfig\SystemConfigService;
  14. use Shopware\Storefront\Framework\Cache\Annotation\HttpCache;
  15. use Shopware\Storefront\Framework\Routing\RequestTransformer;
  16. use Shopware\Storefront\Page\Product\Configurator\ProductCombinationFinder;
  17. use Shopware\Storefront\Page\Product\ProductPageLoadedHook;
  18. use Shopware\Storefront\Page\Product\ProductPageLoader;
  19. use Shopware\Storefront\Page\Product\QuickView\MinimalQuickViewPageLoader;
  20. use Shopware\Storefront\Page\Product\QuickView\ProductQuickViewWidgetLoadedHook;
  21. use Shopware\Storefront\Page\Product\Review\ProductReviewLoader;
  22. use Shopware\Storefront\Page\Product\Review\ProductReviewsWidgetLoadedHook;
  23. use Symfony\Component\HttpFoundation\JsonResponse;
  24. use Symfony\Component\HttpFoundation\Request;
  25. use Symfony\Component\HttpFoundation\Response;
  26. use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
  27. use Symfony\Component\Routing\Annotation\Route;
  28. /**
  29.  * @Route(defaults={"_routeScope"={"storefront"}})
  30.  */
  31. class ProductController extends StorefrontController
  32. {
  33.     /**
  34.      * @var ProductPageLoader
  35.      */
  36.     private $productPageLoader;
  37.     /**
  38.      * @var ProductCombinationFinder
  39.      */
  40.     private $combinationFinder;
  41.     /**
  42.      * @var MinimalQuickViewPageLoader
  43.      */
  44.     private $minimalQuickViewPageLoader;
  45.     /**
  46.      * @var SeoUrlPlaceholderHandlerInterface
  47.      */
  48.     private $seoUrlPlaceholderHandler;
  49.     /**
  50.      * @var ProductReviewLoader
  51.      */
  52.     private $productReviewLoader;
  53.     /**
  54.      * @var SystemConfigService
  55.      */
  56.     private $systemConfigService;
  57.     private AbstractProductReviewSaveRoute $productReviewSaveRoute;
  58.     public function __construct(
  59.         ProductPageLoader $productPageLoader,
  60.         ProductCombinationFinder $combinationFinder,
  61.         MinimalQuickViewPageLoader $minimalQuickViewPageLoader,
  62.         AbstractProductReviewSaveRoute $productReviewSaveRoute,
  63.         SeoUrlPlaceholderHandlerInterface $seoUrlPlaceholderHandler,
  64.         ProductReviewLoader $productReviewLoader,
  65.         SystemConfigService $systemConfigService
  66.     ) {
  67.         $this->productPageLoader $productPageLoader;
  68.         $this->combinationFinder $combinationFinder;
  69.         $this->minimalQuickViewPageLoader $minimalQuickViewPageLoader;
  70.         $this->seoUrlPlaceholderHandler $seoUrlPlaceholderHandler;
  71.         $this->productReviewLoader $productReviewLoader;
  72.         $this->systemConfigService $systemConfigService;
  73.         $this->productReviewSaveRoute $productReviewSaveRoute;
  74.     }
  75.     /**
  76.      * @Since("6.3.3.0")
  77.      * @HttpCache()
  78.      * @Route("/detail/{productId}", name="frontend.detail.page", methods={"GET"})
  79.      */
  80.     public function index(SalesChannelContext $contextRequest $request): Response
  81.     {
  82.         $page $this->productPageLoader->load($request$context);
  83.         $this->hook(new ProductPageLoadedHook($page$context));
  84.         $ratingSuccess $request->get('success');
  85.         // Fallback layout for non-assigned product layout
  86.         if (!$page->getCmsPage()) {
  87.             return $this->renderStorefront('@Storefront/storefront/page/product-detail/index.html.twig', ['page' => $page'ratingSuccess' => $ratingSuccess]);
  88.         }
  89.         return $this->renderStorefront('@Storefront/storefront/page/content/product-detail.html.twig', ['page' => $page]);
  90.     }
  91.     /**
  92.      * @Since("6.0.0.0")
  93.      * @HttpCache()
  94.      * @Route("/detail/{productId}/switch", name="frontend.detail.switch", methods={"GET"}, defaults={"XmlHttpRequest": true})
  95.      */
  96.     public function switch(string $productIdRequest $requestSalesChannelContext $salesChannelContext): JsonResponse
  97.     {
  98.         $switchedOption $request->query->has('switched') ? (string) $request->query->get('switched') : null;
  99.         $options = (string) $request->query->get('options');
  100.         try {
  101.             $newOptions json_decode($optionstrue512, \JSON_THROW_ON_ERROR);
  102.         } catch (\JsonException $jsonException) {
  103.             $newOptions = [];
  104.         }
  105.         try {
  106.             $redirect $this->combinationFinder->find($productId$switchedOption$newOptions$salesChannelContext);
  107.             $productId $redirect->getVariantId();
  108.         } catch (ProductNotFoundException $productNotFoundException) {
  109.             //nth
  110.         }
  111.         $host $request->attributes->get(RequestTransformer::SALES_CHANNEL_ABSOLUTE_BASE_URL)
  112.             . $request->attributes->get(RequestTransformer::SALES_CHANNEL_BASE_URL);
  113.         $url $this->seoUrlPlaceholderHandler->replace(
  114.             $this->seoUrlPlaceholderHandler->generate(
  115.                 'frontend.detail.page',
  116.                 ['productId' => $productId]
  117.             ),
  118.             $host,
  119.             $salesChannelContext
  120.         );
  121.         $response = new JsonResponse([
  122.             'url' => $url,
  123.             'productId' => $productId,
  124.         ]);
  125.         $response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER'1');
  126.         return $response;
  127.     }
  128.     /**
  129.      * @Since("6.0.0.0")
  130.      * @Route("/quickview/{productId}", name="widgets.quickview.minimal", methods={"GET"}, defaults={"XmlHttpRequest": true})
  131.      */
  132.     public function quickviewMinimal(Request $requestSalesChannelContext $context): Response
  133.     {
  134.         $page $this->minimalQuickViewPageLoader->load($request$context);
  135.         $this->hook(new ProductQuickViewWidgetLoadedHook($page$context));
  136.         return $this->renderStorefront('@Storefront/storefront/component/product/quickview/minimal.html.twig', ['page' => $page]);
  137.     }
  138.     /**
  139.      * @Since("6.0.0.0")
  140.      * @Route("/product/{productId}/rating", name="frontend.detail.review.save", methods={"POST"}, defaults={"XmlHttpRequest"=true, "_loginRequired"=true})
  141.      */
  142.     public function saveReview(string $productIdRequestDataBag $dataSalesChannelContext $context): Response
  143.     {
  144.         $this->checkReviewsActive($context);
  145.         try {
  146.             $this->productReviewSaveRoute->save($productId$data$context);
  147.         } catch (ConstraintViolationException $formViolations) {
  148.             return $this->forwardToRoute('frontend.product.reviews', [
  149.                 'productId' => $productId,
  150.                 'success' => -1,
  151.                 'formViolations' => $formViolations,
  152.                 'data' => $data,
  153.             ], ['productId' => $productId]);
  154.         }
  155.         $forwardParams = [
  156.             'productId' => $productId,
  157.             'success' => 1,
  158.             'data' => $data,
  159.             'parentId' => $data->get('parentId'),
  160.         ];
  161.         if ($data->has('id')) {
  162.             $forwardParams['success'] = 2;
  163.         }
  164.         return $this->forwardToRoute('frontend.product.reviews'$forwardParams, ['productId' => $productId]);
  165.     }
  166.     /**
  167.      * @Since("6.0.0.0")
  168.      * @Route("/product/{productId}/reviews", name="frontend.product.reviews", methods={"GET","POST"}, defaults={"XmlHttpRequest"=true})
  169.      */
  170.     public function loadReviews(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  171.     {
  172.         $this->checkReviewsActive($context);
  173.         $reviews $this->productReviewLoader->load($request$context);
  174.         $this->hook(new ProductReviewsWidgetLoadedHook($reviews$context));
  175.         return $this->renderStorefront('storefront/page/product-detail/review/review.html.twig', [
  176.             'reviews' => $reviews,
  177.             'ratingSuccess' => $request->get('success'),
  178.         ]);
  179.     }
  180.     /**
  181.      * @throws ReviewNotActiveExeption
  182.      */
  183.     private function checkReviewsActive(SalesChannelContext $context): void
  184.     {
  185.         $showReview $this->systemConfigService->get('core.listing.showReview'$context->getSalesChannel()->getId());
  186.         if (!$showReview) {
  187.             throw new ReviewNotActiveExeption();
  188.         }
  189.     }
  190. }