vendor/shopware/storefront/Page/Product/ProductPageLoader.php line 74

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Page\Product;
  3. use Shopware\Core\Content\Category\Exception\CategoryNotFoundException;
  4. use Shopware\Core\Content\Cms\CmsPageEntity;
  5. use Shopware\Core\Content\Cms\DataResolver\CmsSlotsDataResolver;
  6. use Shopware\Core\Content\Cms\DataResolver\ResolverContext\EntityResolverContext;
  7. use Shopware\Core\Content\Cms\SalesChannel\SalesChannelCmsPageRepository;
  8. use Shopware\Core\Content\Product\Aggregate\ProductMedia\ProductMediaCollection;
  9. use Shopware\Core\Content\Product\Aggregate\ProductMedia\ProductMediaEntity;
  10. use Shopware\Core\Content\Product\Exception\ProductNotFoundException;
  11. use Shopware\Core\Content\Product\ProductDefinition;
  12. use Shopware\Core\Content\Product\SalesChannel\CrossSelling\AbstractProductCrossSellingRoute;
  13. use Shopware\Core\Content\Product\SalesChannel\Detail\AbstractProductDetailRoute;
  14. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
  15. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionCollection;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  18. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  19. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  20. use Shopware\Storefront\Page\GenericPageLoaderInterface;
  21. use Shopware\Storefront\Page\Product\Review\ProductReviewLoader;
  22. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  23. use Symfony\Component\HttpFoundation\Request;
  24. class ProductPageLoader
  25. {
  26.     private GenericPageLoaderInterface $genericLoader;
  27.     private EventDispatcherInterface $eventDispatcher;
  28.     private SalesChannelCmsPageRepository $cmsPageRepository;
  29.     private CmsSlotsDataResolver $slotDataResolver;
  30.     private ProductDefinition $productDefinition;
  31.     private AbstractProductDetailRoute $productDetailRoute;
  32.     private ProductReviewLoader $productReviewLoader;
  33.     private AbstractProductCrossSellingRoute $crossSellingRoute;
  34.     /**
  35.      * @internal
  36.      */
  37.     public function __construct(
  38.         GenericPageLoaderInterface $genericLoader,
  39.         EventDispatcherInterface $eventDispatcher,
  40.         SalesChannelCmsPageRepository $cmsPageRepository,
  41.         CmsSlotsDataResolver $slotDataResolver,
  42.         ProductDefinition $productDefinition,
  43.         AbstractProductDetailRoute $productDetailRoute,
  44.         ProductReviewLoader $productReviewLoader,
  45.         AbstractProductCrossSellingRoute $crossSellingRoute
  46.     ) {
  47.         $this->genericLoader $genericLoader;
  48.         $this->eventDispatcher $eventDispatcher;
  49.         $this->cmsPageRepository $cmsPageRepository;
  50.         $this->slotDataResolver $slotDataResolver;
  51.         $this->productDefinition $productDefinition;
  52.         $this->productDetailRoute $productDetailRoute;
  53.         $this->productReviewLoader $productReviewLoader;
  54.         $this->crossSellingRoute $crossSellingRoute;
  55.     }
  56.     /**
  57.      * @throws CategoryNotFoundException
  58.      * @throws InconsistentCriteriaIdsException
  59.      * @throws MissingRequestParameterException
  60.      * @throws ProductNotFoundException
  61.      */
  62.     public function load(Request $requestSalesChannelContext $context): ProductPage
  63.     {
  64.         $productId $request->attributes->get('productId');
  65.         if (!$productId) {
  66.             throw new MissingRequestParameterException('productId''/productId');
  67.         }
  68.         $criteria = (new Criteria())
  69.             ->addAssociation('manufacturer.media')
  70.             ->addAssociation('options.group')
  71.             ->addAssociation('properties.group')
  72.             ->addAssociation('mainCategories.category')
  73.             ->addAssociation('media');
  74.         $this->eventDispatcher->dispatch(new ProductPageCriteriaEvent($productId$criteria$context));
  75.         $result $this->productDetailRoute->load($productId$request$context$criteria);
  76.         $product $result->getProduct();
  77.         if ($product->getMedia()) {
  78.             $product->getMedia()->sort(function (ProductMediaEntity $aProductMediaEntity $b) {
  79.                 return $a->getPosition() <=> $b->getPosition();
  80.             });
  81.         }
  82.         if ($product->getMedia() && $product->getCover()) {
  83.             $product->setMedia(new ProductMediaCollection(array_merge(
  84.                 [$product->getCover()->getId() => $product->getCover()],
  85.                 $product->getMedia()->getElements()
  86.             )));
  87.         }
  88.         if ($category $product->getSeoCategory()) {
  89.             $request->request->set('navigationId'$category->getId());
  90.         }
  91.         $page $this->genericLoader->load($request$context);
  92.         $page ProductPage::createFrom($page);
  93.         $page->setProduct($product);
  94.         $page->setConfiguratorSettings($result->getConfigurator());
  95.         $page->setNavigationId($product->getId());
  96.         $this->loadCmsPage($product$page$request$context);
  97.         $this->loadOptions($page);
  98.         $this->loadMetaData($page);
  99.         $this->eventDispatcher->dispatch(
  100.             new ProductPageLoadedEvent($page$context$request)
  101.         );
  102.         return $page;
  103.     }
  104.     private function loadOptions(ProductPage $page): void
  105.     {
  106.         $options = new PropertyGroupOptionCollection();
  107.         $optionIds $page->getProduct()->getOptionIds();
  108.         foreach ($page->getConfiguratorSettings() as $group) {
  109.             $groupOptions $group->getOptions();
  110.             if ($groupOptions === null) {
  111.                 continue;
  112.             }
  113.             foreach ($optionIds as $optionId) {
  114.                 $groupOption $groupOptions->get($optionId);
  115.                 if ($groupOption !== null) {
  116.                     $options->add($groupOption);
  117.                 }
  118.             }
  119.         }
  120.         $page->setSelectedOptions($options);
  121.     }
  122.     private function loadMetaData(ProductPage $page): void
  123.     {
  124.         $metaInformation $page->getMetaInformation();
  125.         if (!$metaInformation) {
  126.             return;
  127.         }
  128.         $metaDescription $page->getProduct()->getTranslation('metaDescription')
  129.             ?? $page->getProduct()->getTranslation('description');
  130.         $metaInformation->setMetaDescription((string) $metaDescription);
  131.         $metaInformation->setMetaKeywords((string) $page->getProduct()->getTranslation('keywords'));
  132.         if ((string) $page->getProduct()->getTranslation('metaTitle') !== '') {
  133.             $metaInformation->setMetaTitle((string) $page->getProduct()->getTranslation('metaTitle'));
  134.             return;
  135.         }
  136.         $metaTitleParts = [$page->getProduct()->getTranslation('name')];
  137.         foreach ($page->getSelectedOptions() as $option) {
  138.             $metaTitleParts[] = $option->getTranslation('name');
  139.         }
  140.         $metaTitleParts[] = $page->getProduct()->getProductNumber();
  141.         $metaInformation->setMetaTitle(implode(' | '$metaTitleParts));
  142.     }
  143.     private function loadSlotData(
  144.         CmsPageEntity $page,
  145.         SalesChannelContext $salesChannelContext,
  146.         SalesChannelProductEntity $product,
  147.         Request $request
  148.     ): void {
  149.         $resolverContext = new EntityResolverContext($salesChannelContext$request$this->productDefinition$product);
  150.         foreach ($page->getSections() as $section) {
  151.             $slots $this->slotDataResolver->resolve($section->getBlocks()->getSlots(), $resolverContext);
  152.             $section->getBlocks()->setSlots($slots);
  153.         }
  154.     }
  155.     private function getCmsPage(string $cmsPageIdSalesChannelContext $context): ?CmsPageEntity
  156.     {
  157.         $pages $this->cmsPageRepository->read([$cmsPageId], $context);
  158.         if ($pages->count() === 0) {
  159.             return null;
  160.         }
  161.         /** @var CmsPageEntity $page */
  162.         $page $pages->first();
  163.         return $page;
  164.     }
  165.     private function loadCmsPage(SalesChannelProductEntity $productProductPage $pageRequest $requestSalesChannelContext $context): void
  166.     {
  167.         if ($cmsPage $product->getCmsPage()) {
  168.             $page->setCmsPage($cmsPage);
  169.             return;
  170.         }
  171.         $request->request->set('parentId'$product->getParentId());
  172.         $reviews $this->productReviewLoader->load($request$context);
  173.         $reviews->setParentId($product->getParentId() ?? $product->getId());
  174.         $page->setReviews($reviews);
  175.         $crossSellings $this->crossSellingRoute->load($product->getId(), new Request(), $context, new Criteria());
  176.         $page->setCrossSellings($crossSellings->getResult());
  177.         if ($product->getCmsPageId() === null) {
  178.             return;
  179.         }
  180.         $cmsPage $this->getCmsPage($product->getCmsPageId(), $context);
  181.         if ($cmsPage === null) {
  182.             return;
  183.         }
  184.         $this->loadSlotData($cmsPage$context$product$request);
  185.         $page->setCmsPage($cmsPage);
  186.     }
  187. }