<?php
declare(strict_types=1);
namespace WbfkExtensions\Core\Content\ProductComparison;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Exception;
use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotCollection;
use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
use Shopware\Core\Content\Cms\DataResolver\CmsSlotsDataResolver;
use Shopware\Core\Content\Cms\DataResolver\ResolverContext\ResolverContext;
use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\Framework\Uuid\Uuid;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class ProductComparisonService
{
private ?ProductForComparisonCollection $productForComparisonCollection = null;
public function __construct(
private readonly SalesChannelRepository $productRepository,
private readonly CmsSlotsDataResolver $cmsSlotsDataResolver,
private readonly Connection $connection,
private readonly SystemConfigService $systemConfigService
) {
}
public function getComparisonList(
SessionInterface $session
): ProductForComparisonCollection {
return $this->productForComparisonCollection ?? $this->loadProductForComparisonCollection($session);
}
private function loadProductForComparisonCollection(
SessionInterface $session
): ProductForComparisonCollection {
$this->productForComparisonCollection = $session->get(
'productIdsForComparison',
new ProductForComparisonCollection()
);
$session->set('productIdsForComparison', $this->productForComparisonCollection);
return $this->productForComparisonCollection;
}
public function addProduct(SessionInterface $session, SalesChannelContext $context, string $productId): void
{
$maximumProductsToCompare = (int)$this->systemConfigService->get(
'WbfkExtensions.config.maximumProductsToCompare'
);
if ($this->getComparisonList($session)->count() === $maximumProductsToCompare) {
throw new MaximumProductsToCompareReachedException($maximumProductsToCompare);
}
$criteria = new Criteria([$productId]);
$criteria->addAssociation('cover');
/** @var SalesChannelProductEntity $product */
$product = $this->productRepository->search($criteria, $context)->getEntities()->first();
$productForComparison = new ProductForComparison(
$product->getId(),
$product->getName(),
$product->getProductNumber(),
$product->getCover()?->getMedia()->getUrl()
);
$this->getComparisonList($session)->set(
$productForComparison->id,
$productForComparison
);
}
public function removeProduct(SessionInterface $session, string $productId): void
{
$this->getComparisonList($session)->remove($productId);
}
/**
* @throws Exception
* @throws \Doctrine\DBAL\Exception
*/
public function getProductCmsSlotData(
Request $request,
SalesChannelContext $context
) {
$productKeys = $this->convertProductNumbersToIds($request->get('products', ''), $context);
$cmsSlot = new CmsSlotEntity();
$cmsSlot->setId('manual-product-comparison');
$cmsSlot->setType('product-comparison');
$cmsSlot->setSlot('productCompare');
$cmsSlot->setBlockId('manual-product-comparison-block');
$cmsSlot->setConfig([
"productComparisonType" => [
"source" => "static",
"value" => "variants",
],
"productAssignmentType" => [
"source" => "static",
"value" => "static",
],
"products" => [
"source" => "static",
"value" => array_values($productKeys),
],
"productStreamSorting" => [
"source" => "static",
"value" => "name:ASC",
],
"productStreamLimit" => [
"source" => "static",
"value" => 10,
],
"productProperties" => [
"source" => "static",
"value" => $this->getProductProperties($productKeys),
],
]);
$cmsSlot->setTranslated([
'config' => $cmsSlot->getConfig(),
]);
$cmsSlotCollection = new CmsSlotCollection([$cmsSlot]);
$enrichedCmsSlotCollection = $this->cmsSlotsDataResolver->resolve(
$cmsSlotCollection,
new ResolverContext($context, $request)
);
return $enrichedCmsSlotCollection->first();
}
/**
* @throws Exception
* @throws \Doctrine\DBAL\Exception
*/
private function getProductProperties(array $productKeys): array
{
foreach ($productKeys as $key => $value) {
$productKeys[$key] = Uuid::fromHexToBytes($value);
}
return $this->connection->executeQuery(
<<<SQL
SELECT DISTINCT(LOWER(HEX(property_group.id))) AS id
FROM property_group
LEFT JOIN property_group_option ON property_group_option.property_group_id = property_group.id
LEFT JOIN product_property ON product_property.property_group_option_id = property_group_option.id
WHERE product_property.product_id IN (:productIds)
SQL,
[
':productIds' => $productKeys,
],
[
':productIds' => Connection::PARAM_STR_ARRAY,
]
)->fetchFirstColumn();
}
private function convertProductNumbersToIds(string $productNumbersCombined, SalesChannelContext $context): array
{
$productNumbers = explode(',', $productNumbersCombined);
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('productNumber', $productNumbers));
$productIds = $this->productRepository->searchIds($criteria, $context);
return $productIds->getIds();
}
}