<?php declare(strict_types=1);
namespace EmcgnStandardCmsPages\Core\Content\Product;
use Shopware\Core\Content\Category\Service\CategoryBreadcrumbBuilder;
use Shopware\Core\Content\Cms\DataResolver\ResolverContext\EntityResolverContext;
use Shopware\Core\Content\Cms\SalesChannel\SalesChannelCmsPageLoaderInterface;
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
use Shopware\Core\Content\Product\Exception\ProductNotFoundException;
use Shopware\Core\Content\Product\SalesChannel\Detail\AbstractProductDetailRoute;
use Shopware\Core\Content\Product\SalesChannel\Detail\ProductConfiguratorLoader;
use Shopware\Core\Content\Product\SalesChannel\Detail\ProductDetailRouteResponse;
use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
use Shopware\Core\Content\Product\SalesChannel\ProductCloseoutFilter;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductDefinition;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
class EmcgnProductDetailRoute extends AbstractProductDetailRoute
{
private Container $container;
private SystemConfigService $config;
private ProductConfiguratorLoader $configuratorLoader;
private CategoryBreadcrumbBuilder $breadcrumbBuilder;
private SalesChannelCmsPageLoaderInterface $cmsPageLoader;
private SalesChannelProductDefinition $productDefinition;
private EntityRepository $entityRepository;
public function __construct(
Container $container,
SystemConfigService $config,
ProductConfiguratorLoader $configuratorLoader,
CategoryBreadcrumbBuilder $breadcrumbBuilder,
SalesChannelCmsPageLoaderInterface $cmsPageLoader,
SalesChannelProductDefinition $productDefinition,
EntityRepository $entityRepository
) {
$this->container = $container;
$this->config = $config;
$this->configuratorLoader = $configuratorLoader;
$this->breadcrumbBuilder = $breadcrumbBuilder;
$this->cmsPageLoader = $cmsPageLoader;
$this->productDefinition = $productDefinition;
$this->entityRepository = $entityRepository;
$this->productRepository = $this->container->get('sales_channel.product.repository');
}
public function getDecorated(): AbstractProductDetailRoute
{
throw new DecorationPatternException(self::class);
}
public function load(string $productId, Request $request, SalesChannelContext $context, Criteria $criteria): ProductDetailRouteResponse
{
$productId = $this->findBestVariant($productId, $context);
$this->addFilters($context, $criteria);
$criteria->setIds([$productId]);
$product = $this->productRepository
->search($criteria, $context)
->first();
if (!$product instanceof SalesChannelProductEntity) {
throw new ProductNotFoundException($productId);
}
$product->setSeoCategory(
$this->breadcrumbBuilder->getProductSeoCategory($product, $context)
);
$configurator = $this->configuratorLoader->load($product, $context);
$pageId = $product->getCmsPageId();
if ($pageId) {
$this->setCmsPage($request, $pageId, $context, $product);
} else {
$pageId = $this->config->get('EmcgnStandardCmsPages.config.selectedCmsProductPage');
if (!empty($pageId)) {
$frameworkContext = $context->getContext();
$cmsPage = $this->entityRepository->search(new Criteria([$pageId]), $frameworkContext)->first();
if (!empty($cmsPage)) {
$cmsType = $cmsPage->getType();
if($cmsType == 'product_detail') {
$this->setCmsPage($request, $pageId, $context, $product);
}
}
}
}
return new ProductDetailRouteResponse($product, $configurator);
}
/**
* @param string $productId
* @param SalesChannelContext $context
* @return string
*/
private function findBestVariant(string $productId, SalesChannelContext $context): string
{
$criteria = (new Criteria())
->addFilter(new EqualsFilter('product.parentId', $productId))
->addSorting(new FieldSorting('product.price'))
->addSorting(new FieldSorting('product.available'))
->setLimit(1);
$variantId = $this->productRepository->searchIds($criteria, $context);
return $variantId->firstId() ?? $productId;
}
/**
* @param SalesChannelContext $context
* @param Criteria $criteria
*/
private function addFilters(SalesChannelContext $context, Criteria $criteria): void
{
$criteria->addFilter(
new ProductAvailableFilter($context->getSalesChannel()->getId(), ProductVisibilityDefinition::VISIBILITY_LINK)
);
$salesChannelId = $context->getSalesChannel()->getId();
$hideCloseoutProductsWhenOutOfStock = $this->config->get('core.listing.hideCloseoutProductsWhenOutOfStock', $salesChannelId);
if ($hideCloseoutProductsWhenOutOfStock) {
$filter = new ProductCloseoutFilter();
$filter->addQuery(new EqualsFilter('product.parentId', null));
$criteria->addFilter($filter);
}
}
/**
* @param $request
* @param $pageId
* @param $context
* @param $product
*/
private function setCmsPage($request, $pageId, $context, $product)
{
$resolverContext = new EntityResolverContext($context, $request, $this->productDefinition, clone $product);
$pages = $this->cmsPageLoader->load(
$request,
$this->createCriteria($pageId, $request),
$context,
$product->getTranslation('slotConfig'),
$resolverContext
);
if ($page = $pages->first()) {
$product->setCmsPage($page);
}
}
/**
* @param string $pageId
* @param Request $request
* @return Criteria
*/
private function createCriteria(string $pageId, Request $request): Criteria
{
$criteria = new Criteria([$pageId]);
$criteria->setTitle('product::cms-page');
$slots = $request->get('slots');
if (\is_string($slots)) {
$slots = explode('|', $slots);
}
if (!empty($slots) && \is_array($slots)) {
$criteria
->getAssociation('sections.blocks')
->addFilter(new EqualsAnyFilter('slots.id', $slots));
}
return $criteria;
}
}