platform/src/Storefront/Controller/ProductController.php line 88

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