vendor/shopware/storefront/Controller/ProductController.php line 98

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.  * @deprecated tag:v6.5.0 - reason:becomes-internal - Will be internal
  32.  */
  33. class ProductController extends StorefrontController
  34. {
  35.     /**
  36.      * @var ProductPageLoader
  37.      */
  38.     private $productPageLoader;
  39.     /**
  40.      * @var ProductCombinationFinder
  41.      */
  42.     private $combinationFinder;
  43.     /**
  44.      * @var MinimalQuickViewPageLoader
  45.      */
  46.     private $minimalQuickViewPageLoader;
  47.     /**
  48.      * @var SeoUrlPlaceholderHandlerInterface
  49.      */
  50.     private $seoUrlPlaceholderHandler;
  51.     /**
  52.      * @var ProductReviewLoader
  53.      */
  54.     private $productReviewLoader;
  55.     /**
  56.      * @var SystemConfigService
  57.      */
  58.     private $systemConfigService;
  59.     private AbstractProductReviewSaveRoute $productReviewSaveRoute;
  60.     /**
  61.      * @internal
  62.      */
  63.     public function __construct(
  64.         ProductPageLoader $productPageLoader,
  65.         ProductCombinationFinder $combinationFinder,
  66.         MinimalQuickViewPageLoader $minimalQuickViewPageLoader,
  67.         AbstractProductReviewSaveRoute $productReviewSaveRoute,
  68.         SeoUrlPlaceholderHandlerInterface $seoUrlPlaceholderHandler,
  69.         ProductReviewLoader $productReviewLoader,
  70.         SystemConfigService $systemConfigService
  71.     ) {
  72.         $this->productPageLoader $productPageLoader;
  73.         $this->combinationFinder $combinationFinder;
  74.         $this->minimalQuickViewPageLoader $minimalQuickViewPageLoader;
  75.         $this->seoUrlPlaceholderHandler $seoUrlPlaceholderHandler;
  76.         $this->productReviewLoader $productReviewLoader;
  77.         $this->systemConfigService $systemConfigService;
  78.         $this->productReviewSaveRoute $productReviewSaveRoute;
  79.     }
  80.     /**
  81.      * @Since("6.3.3.0")
  82.      * @HttpCache()
  83.      * @Route("/detail/{productId}", name="frontend.detail.page", methods={"GET"})
  84.      */
  85.     public function index(SalesChannelContext $contextRequest $request): Response
  86.     {
  87.         $page $this->productPageLoader->load($request$context);
  88.         $this->hook(new ProductPageLoadedHook($page$context));
  89.         $ratingSuccess $request->get('success');
  90.         // Fallback layout for non-assigned product layout
  91.         if (!$page->getCmsPage()) {
  92.             return $this->renderStorefront('@Storefront/storefront/page/product-detail/index.html.twig', ['page' => $page'ratingSuccess' => $ratingSuccess]);
  93.         }
  94.         return $this->renderStorefront('@Storefront/storefront/page/content/product-detail.html.twig', ['page' => $page]);
  95.     }
  96.     /**
  97.      * @Since("6.0.0.0")
  98.      * @HttpCache()
  99.      * @Route("/detail/{productId}/switch", name="frontend.detail.switch", methods={"GET"}, defaults={"XmlHttpRequest": true})
  100.      */
  101.     public function switch(string $productIdRequest $requestSalesChannelContext $salesChannelContext): JsonResponse
  102.     {
  103.         $switchedOption $request->query->has('switched') ? (string) $request->query->get('switched') : null;
  104.         $options = (string) $request->query->get('options');
  105.         try {
  106.             $newOptions json_decode($optionstrue512, \JSON_THROW_ON_ERROR);
  107.         } catch (\JsonException $jsonException) {
  108.             $newOptions = [];
  109.         }
  110.         try {
  111.             $redirect $this->combinationFinder->find($productId$switchedOption$newOptions$salesChannelContext);
  112.             $productId $redirect->getVariantId();
  113.         } catch (ProductNotFoundException $productNotFoundException) {
  114.             //nth
  115.         }
  116.         $host $request->attributes->get(RequestTransformer::SALES_CHANNEL_ABSOLUTE_BASE_URL)
  117.             . $request->attributes->get(RequestTransformer::SALES_CHANNEL_BASE_URL);
  118.         $url $this->seoUrlPlaceholderHandler->replace(
  119.             $this->seoUrlPlaceholderHandler->generate(
  120.                 'frontend.detail.page',
  121.                 ['productId' => $productId]
  122.             ),
  123.             $host,
  124.             $salesChannelContext
  125.         );
  126.         $response = new JsonResponse([
  127.             'url' => $url,
  128.             'productId' => $productId,
  129.         ]);
  130.         $response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER'1');
  131.         return $response;
  132.     }
  133.     /**
  134.      * @Since("6.0.0.0")
  135.      * @Route("/quickview/{productId}", name="widgets.quickview.minimal", methods={"GET"}, defaults={"XmlHttpRequest": true})
  136.      */
  137.     public function quickviewMinimal(Request $requestSalesChannelContext $context): Response
  138.     {
  139.         $page $this->minimalQuickViewPageLoader->load($request$context);
  140.         $this->hook(new ProductQuickViewWidgetLoadedHook($page$context));
  141.         return $this->renderStorefront('@Storefront/storefront/component/product/quickview/minimal.html.twig', ['page' => $page]);
  142.     }
  143.     /**
  144.      * @Since("6.0.0.0")
  145.      * @Route("/product/{productId}/rating", name="frontend.detail.review.save", methods={"POST"}, defaults={"XmlHttpRequest"=true, "_loginRequired"=true})
  146.      */
  147.     public function saveReview(string $productIdRequestDataBag $dataSalesChannelContext $context): Response
  148.     {
  149.         $this->checkReviewsActive($context);
  150.         try {
  151.             $this->productReviewSaveRoute->save($productId$data$context);
  152.         } catch (ConstraintViolationException $formViolations) {
  153.             return $this->forwardToRoute('frontend.product.reviews', [
  154.                 'productId' => $productId,
  155.                 'success' => -1,
  156.                 'formViolations' => $formViolations,
  157.                 'data' => $data,
  158.             ], ['productId' => $productId]);
  159.         }
  160.         $forwardParams = [
  161.             'productId' => $productId,
  162.             'success' => 1,
  163.             'data' => $data,
  164.             'parentId' => $data->get('parentId'),
  165.         ];
  166.         if ($data->has('id')) {
  167.             $forwardParams['success'] = 2;
  168.         }
  169.         return $this->forwardToRoute('frontend.product.reviews'$forwardParams, ['productId' => $productId]);
  170.     }
  171.     /**
  172.      * @Since("6.0.0.0")
  173.      * @Route("/product/{productId}/reviews", name="frontend.product.reviews", methods={"GET","POST"}, defaults={"XmlHttpRequest"=true})
  174.      */
  175.     public function loadReviews(Request $requestRequestDataBag $dataSalesChannelContext $context): Response
  176.     {
  177.         $this->checkReviewsActive($context);
  178.         $reviews $this->productReviewLoader->load($request$context);
  179.         $this->hook(new ProductReviewsWidgetLoadedHook($reviews$context));
  180.         return $this->renderStorefront('storefront/page/product-detail/review/review.html.twig', [
  181.             'reviews' => $reviews,
  182.             'ratingSuccess' => $request->get('success'),
  183.         ]);
  184.     }
  185.     /**
  186.      * @throws ReviewNotActiveExeption
  187.      */
  188.     private function checkReviewsActive(SalesChannelContext $context): void
  189.     {
  190.         $showReview $this->systemConfigService->get('core.listing.showReview'$context->getSalesChannel()->getId());
  191.         if (!$showReview) {
  192.             throw new ReviewNotActiveExeption();
  193.         }
  194.     }
  195. }