<?php declare(strict_types=1);
namespace Intedia\Doofinder\Core\Content\ProductExport\Subscriber;
use Shopware\Core\Content\Category\CategoryDefinition;
use Shopware\Core\Content\Category\CategoryEntity;
use Shopware\Core\Content\Category\Service\NavigationLoader;
use Shopware\Core\Content\Category\Tree\Tree;
use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
use Shopware\Core\Content\Product\ProductEntity;
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\Exception\NoFilterException;
use Shopware\Core\Content\ProductStream\Service\ProductStreamBuilder;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ProductExportSubscriber implements EventSubscriberInterface
{
protected NavigationLoader $navigationLoader;
protected ProductStreamBuilder $productStreamBuilder;
protected EntityRepository $categoryRepository;
protected EntityRepository $productRepository;
protected ?Tree $categoryTree = null;
protected array $productStreamCategories = [];
protected array $products = [];
/**
* @param NavigationLoader $navigationLoader
* @param ProductStreamBuilder $productStreamBuilder
* @param EntityRepository $categoryRepository
* @param EntityRepository $productRepository
*/
public function __construct(NavigationLoader $navigationLoader, ProductStreamBuilder $productStreamBuilder, EntityRepository $categoryRepository, EntityRepository $productRepository)
{
$this->navigationLoader = $navigationLoader;
$this->productStreamBuilder = $productStreamBuilder;
$this->categoryRepository = $categoryRepository;
$this->productRepository = $productRepository;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array
{
return [
ProductExportProductCriteriaEvent::class => 'onProductExportCriteriaEvent',
ProductExportRenderBodyContextEvent::class => 'onProductExportRenderBodyContextEvent'
];
}
/**
* @param ProductExportProductCriteriaEvent $event
* @throws \Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException
*/
public function onProductExportCriteriaEvent(ProductExportProductCriteriaEvent $event): void
{
$criteria = $event->getCriteria();
$productExport = $event->getProductExport();
$context = $event->getSalesChannelContext();
// add data for DooFinderCategories
if ($this->usesDooFinderCategoriesExtension($productExport)) {
$this->addDooFinderCategoriesData($criteria, $context);
}
if ($this->usesDooFinderGroupIdExtension($productExport)) {
$this->addDooFinderGroupIdData($criteria, $context);
}
}
protected function addDooFinderGroupIdData(Criteria $criteria, SalesChannelContext $context): void
{
if ($context->getSalesChannel()) {
$this->products = $this->getProducts($context);
}
}
/**
* @param Criteria $criteria
* @param SalesChannelContext $context
*/
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();
}
}
}
/**
* @param ProductExportRenderBodyContextEvent $event
*/
public function onProductExportRenderBodyContextEvent(ProductExportRenderBodyContextEvent $event): void
{
$context = $event->getContext();
$context['groupIds'] = $this->products;
$context['categoryTree'] = $this->categoryTree;
$context['productStreamCategories'] = $this->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->productRepository->searchIds($criteria, $salesChannelContext->getContext())->getIds();
}
catch (NoFilterException $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 getProducts($context): array
{
$criteria = new Criteria();
$criteria->addAssociation('children');
$products = $this->productRepository->search($criteria, Context::createDefaultContext());
$productArray = [];
/** @var ProductEntity $product */
foreach ($products as $product) {
$productArray[$product->getId()] = $product->getProductNumber();
}
return $productArray;
}
}