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

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