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

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