vendor/shopware/storefront/Page/Product/ProductPageLoader.php line 49
<?php declare(strict_types=1);namespace Shopware\Storefront\Page\Product;use Shopware\Core\Content\Category\Exception\CategoryNotFoundException;use Shopware\Core\Content\Product\Aggregate\ProductMedia\ProductMediaCollection;use Shopware\Core\Content\Product\Aggregate\ProductMedia\ProductMediaEntity;use Shopware\Core\Content\Product\Exception\ProductNotFoundException;use Shopware\Core\Content\Product\SalesChannel\CrossSelling\AbstractProductCrossSellingRoute;use Shopware\Core\Content\Product\SalesChannel\Detail\AbstractProductDetailRoute;use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionCollection;use Shopware\Core\Content\Property\PropertyGroupCollection;use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;use Shopware\Core\Framework\Feature;use Shopware\Core\Framework\Log\Package;use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;use Shopware\Core\System\SalesChannel\SalesChannelContext;use Shopware\Storefront\Page\GenericPageLoaderInterface;use Shopware\Storefront\Page\Product\Review\ProductReviewLoader;use Symfony\Component\EventDispatcher\EventDispatcherInterface;use Symfony\Component\HttpFoundation\Request;/*** Do not use direct or indirect repository calls in a PageLoader. Always use a store-api route to get or put data.*/#[Package('storefront')]class ProductPageLoader{/*** @internal*/public function __construct(private readonly GenericPageLoaderInterface $genericLoader,private readonly EventDispatcherInterface $eventDispatcher,private readonly AbstractProductDetailRoute $productDetailRoute,private readonly ProductReviewLoader $productReviewLoader,private readonly AbstractProductCrossSellingRoute $crossSellingRoute) {}/*** @throws CategoryNotFoundException* @throws InconsistentCriteriaIdsException* @throws MissingRequestParameterException* @throws ProductNotFoundException*/public function load(Request $request, SalesChannelContext $context): ProductPage{$productId = $request->attributes->get('productId');if (!$productId) {throw new MissingRequestParameterException('productId', '/productId');}$criteria = (new Criteria())->addAssociation('manufacturer.media')->addAssociation('options.group')->addAssociation('properties.group')->addAssociation('mainCategories.category')->addAssociation('media');$this->eventDispatcher->dispatch(new ProductPageCriteriaEvent($productId, $criteria, $context));$result = $this->productDetailRoute->load($productId, $request, $context, $criteria);$product = $result->getProduct();if ($product->getMedia()) {$product->getMedia()->sort(fn (ProductMediaEntity $a, ProductMediaEntity $b) => $a->getPosition() <=> $b->getPosition());}if ($product->getMedia() && $product->getCover()) {$product->setMedia(new ProductMediaCollection(array_merge([$product->getCover()->getId() => $product->getCover()],$product->getMedia()->getElements())));}if ($category = $product->getSeoCategory()) {$request->request->set('navigationId', $category->getId());}$page = $this->genericLoader->load($request, $context);$page = ProductPage::createFrom($page);$page->setProduct($product);$page->setConfiguratorSettings($result->getConfigurator() ?? new PropertyGroupCollection());$page->setNavigationId($product->getId());if (!Feature::isActive('v6.6.0.0')) {$this->loadDefaultAdditions($product, $page, $request, $context);} elseif ($cmsPage = $product->getCmsPage()) {$page->setCmsPage($cmsPage);}$this->loadOptions($page);$this->loadMetaData($page);$this->eventDispatcher->dispatch(new ProductPageLoadedEvent($page, $context, $request));return $page;}private function loadOptions(ProductPage $page): void{$options = new PropertyGroupOptionCollection();if (($optionIds = $page->getProduct()->getOptionIds()) === null) {$page->setSelectedOptions($options);return;}foreach ($page->getConfiguratorSettings() as $group) {$groupOptions = $group->getOptions();if ($groupOptions === null) {continue;}foreach ($optionIds as $optionId) {$groupOption = $groupOptions->get($optionId);if ($groupOption !== null) {$options->add($groupOption);}}}$page->setSelectedOptions($options);}private function loadMetaData(ProductPage $page): void{$metaInformation = $page->getMetaInformation();if (!$metaInformation) {return;}$metaDescription = $page->getProduct()->getTranslation('metaDescription')?? $page->getProduct()->getTranslation('description');$metaInformation->setMetaDescription((string) $metaDescription);$metaInformation->setMetaKeywords((string) $page->getProduct()->getTranslation('keywords'));if ((string) $page->getProduct()->getTranslation('metaTitle') !== '') {$metaInformation->setMetaTitle((string) $page->getProduct()->getTranslation('metaTitle'));return;}$metaTitleParts = [$page->getProduct()->getTranslation('name')];foreach ($page->getSelectedOptions() as $option) {$metaTitleParts[] = $option->getTranslation('name');}$metaTitleParts[] = $page->getProduct()->getProductNumber();$metaInformation->setMetaTitle(implode(' | ', $metaTitleParts));}/*** @@deprecated tag:v6.6.0 - will be removed because cms page id will always be set*/private function loadDefaultAdditions(SalesChannelProductEntity $product, ProductPage $page, Request $request, SalesChannelContext $context): void{if ($cmsPage = $product->getCmsPage()) {$page->setCmsPage($cmsPage);return;}$request->request->set('parentId', $product->getParentId());$reviews = $this->productReviewLoader->load($request, $context);$reviews->setParentId($product->getParentId() ?? $product->getId());$page->setReviews($reviews);$crossSellings = $this->crossSellingRoute->load($product->getId(), new Request(), $context, new Criteria());$page->setCrossSellings($crossSellings->getResult());}}