<?php declare(strict_types=1);
namespace Intedia\Doofinder\Core\Content\ProductExport\Subscriber;
use Doctrine\DBAL\Connection;
use Shopware\Core\Content\Category\CategoryDefinition;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Category\Service\NavigationLoaderInterface;
use Shopware\Core\Content\Category\Tree\Tree;
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
use Shopware\Core\Content\ProductExport\Event\ProductExportProductCriteriaEvent;
use Shopware\Core\Content\ProductExport\Event\ProductExportRenderBodyContextEvent;
use Shopware\Core\Content\ProductExport\ProductExportEntity;
use Shopware\Core\Content\ProductStream\Service\ProductStreamBuilderInterface;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProductExportSubscriber implements EventSubscriberInterface
{
protected NavigationLoaderInterface $navigationLoader;
protected ProductStreamBuilderInterface $productStreamBuilder;
protected EntityRepositoryInterface $categoryRepository;
protected Connection $connection;
protected SalesChannelRepositoryInterface $salesChannelRepository;
protected ?Tree $categoryTree = null;
protected array $productStreamCategories = [];
protected array $productNumberIdMapping = [];
protected bool $usesDooFinderGroupIdExtension = false;
protected bool $usesDooFinderCategoriesExtension = false;
protected bool $runOnCriteriaEvent = false;
public function __construct(NavigationLoaderInterface $navigationLoader, ProductStreamBuilderInterface $productStreamBuilder, EntityRepositoryInterface $categoryRepository, Connection $connection, SalesChannelRepositoryInterface $salesChannelRepository)
{
$this->navigationLoader = $navigationLoader;
$this->productStreamBuilder = $productStreamBuilder;
$this->categoryRepository = $categoryRepository;
$this->connection = $connection;
$this->salesChannelRepository = $salesChannelRepository;
}
public static function getSubscribedEvents(): array
{
return [
ProductExportProductCriteriaEvent::class => 'onProductExportCriteriaEvent',
ProductExportRenderBodyContextEvent::class => 'onProductExportRenderBodyContextEvent'
];
}
public function onProductExportCriteriaEvent(ProductExportProductCriteriaEvent $event): void
{
$criteria = $event->getCriteria();
$productExport = $event->getProductExport();
$context = $event->getSalesChannelContext();
$this->usesDooFinderCategoriesExtension = $this->usesDooFinderCategoriesExtension($productExport);
$this->usesDooFinderGroupIdExtension = $this->usesDooFinderGroupIdExtension($productExport);
if ($this->usesDooFinderCategoriesExtension || $this->usesDooFinderGroupIdExtension) {
$this->raiseMemoryLimit();
if ($this->usesDooFinderCategoriesExtension) {
$this->addDooFinderCategoriesData($criteria, $context);
}
if ($this->usesDooFinderGroupIdExtension) {
$this->addDooFinderGroupIdData();
}
}
$this->runOnCriteriaEvent = true;
}
protected function addDooFinderGroupIdData(): void
{
$this->productNumberIdMapping = $this->getProductNumberIdMapping();
}
protected function addDooFinderCategoriesData(Criteria $criteria, SalesChannelContext $context): void
{
if (!$criteria->hasAssociation('categories')) {
$criteria->addAssociation('categories');
}
if ($context->getSalesChannel()) {
$navigationCategoryId = $context->getSalesChannel()->getNavigationCategoryId();
$this->categoryTree = $this->navigationLoader->load($navigationCategoryId, $context, $navigationCategoryId, 10);
$this->buildProductStreamCategories($context);
}
}
protected function buildProductStreamCategories(SalesChannelContext $context)
{
foreach ($this->getCategoriesHavingStreams($context->getContext()) as $category)
{
foreach ($this->getProductIdsInStream($category, $context) as $productId)
{
if (!array_key_exists($productId, $this->productStreamCategories)) {
$this->productStreamCategories[$productId] = [];
}
$this->productStreamCategories[$productId][] = $category->getId();
}
}
}
public function onProductExportRenderBodyContextEvent(ProductExportRenderBodyContextEvent $event): void
{
$context = $event->getContext();
if ($this->runOnCriteriaEvent) {
if ($this->usesDooFinderGroupIdExtension) {
$context['groupIds'] = $this->productNumberIdMapping ?: [];
}
if ($this->usesDooFinderCategoriesExtension) {
$context['categoryTree'] = $this->categoryTree ?: null;
$context['productStreamCategories'] = $this->productStreamCategories ?: [];
}
}
else {
if (!array_key_exists('groupIds', $context)) {
$context['groupIds'] = [];
}
if (!array_key_exists('categoryTree', $context)) {
$context['categoryTree'] = null;
}
if (!array_key_exists('productStreamCategories', $context)) {
$context['productStreamCategories'] = [];
}
}
$event->setContext($context);
}
protected function getProductIdsInStream(CategoryEntity $category, SalesChannelContext $salesChannelContext): array
{
if (is_null($category->getProductStreamId())) {
return [];
}
try {
$filters = $this->productStreamBuilder->buildFilters($category->getProductStreamId(), $salesChannelContext->getContext());
$criteria = new Criteria();
$criteria->addFilter(...$filters);
$criteria->addFilter(
new ProductAvailableFilter($salesChannelContext->getSalesChannel()->getId(), ProductVisibilityDefinition::VISIBILITY_ALL)
);
return $this->salesChannelRepository->searchIds($criteria, $salesChannelContext)->getIds();
}
catch (\Exception $exception) {
}
return [];
}
protected function getCategoriesHavingStreams(Context $context)
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('productAssignmentType', CategoryDefinition::PRODUCT_ASSIGNMENT_TYPE_PRODUCT_STREAM));
return $this->categoryRepository->search($criteria, $context)->getEntities();
}
protected function usesDooFinderCategoriesExtension(ProductExportEntity $productExport): bool
{
return strpos($productExport->getBodyTemplate(), 'doofinderCategories') !== false;
}
protected function usesDooFinderGroupIdExtension(ProductExportEntity $productExport): bool
{
return strpos($productExport->getBodyTemplate(), 'doofinderGroupId') !== false;
}
protected function getProductNumberIdMapping(): array
{
return $this->connection->fetchAllKeyValue("SELECT lower(hex(id)), product_number from product");
}
protected function raiseMemoryLimit()
{
ini_set('memory_limit', '5G');
}
}