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

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