custom/plugins/RhiemUserProducts/src/Subscriber/OnNavigationLoadedEvent.php line 112

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Rhiem\RhiemUserProducts\Subscriber;
  4. use Doctrine\DBAL\Connection;
  5. use Shopware\Core\Framework\Context;
  6. use Shopware\Core\Framework\Uuid\Uuid;
  7. use Shopware\Core\Framework\Struct\ArrayStruct;
  8. use Shopware\Core\Content\Category\Tree\TreeItem;
  9. use Symfony\Component\DependencyInjection\Container;
  10. use Shopware\Core\Content\Category\CategoryDefinition;
  11. use Shopware\Core\System\SystemConfig\SystemConfigService;
  12. use Shopware\Core\Content\Category\Event\NavigationLoadedEvent;
  13. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  15. use Rhiem\RhiemUserProducts\Entities\ProductGroup\ProductGroupEntity;
  16. use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
  17. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  19. use Shopware\Core\Content\ProductStream\Service\ProductStreamBuilderInterface;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\ContainsFilter;
  21. use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
  22. class OnNavigationLoadedEvent implements EventSubscriberInterface
  23. {
  24.     /**
  25.      * @var EntityRepositoryInterface
  26.      */
  27.     private $productGroupRepository;
  28.     /**
  29.      * @var SystemConfigService
  30.      */
  31.     private $systemConfigService;
  32.     /**
  33.      * @var ProductStreamBuilderInterface
  34.      */
  35.     private $productStreamBuilder;
  36.     /**
  37.      * @var EntityRepositoryInterface
  38.      */
  39.     private $productRepository;
  40.     /**
  41.      * @var Container
  42.      */
  43.     private $container;
  44.     /**
  45.      * @var string
  46.      */
  47.     private $productGroupId;
  48.     /**
  49.      * @var ProductGroupEntity|null
  50.      */
  51.     private $productGroup;
  52.     /**
  53.      * @var array|null
  54.      */
  55.     private $categories;
  56.     /**
  57.      * @var array|null
  58.      */
  59.     private $productGroupCategoryIds;
  60.     private $blacklistProductIds;
  61.     public const FALLBACK_PRODUCT_GROUP_ID '0502913fa0a54ee7b24a4128fb2c62bb';
  62.     /**
  63.      * @param EntityRepositoryInterface $productGroupRepository
  64.      * @param SystemConfigService $systemConfigService
  65.      * @param EntityRepositoryInterface $productRepository
  66.      * @param ProductStreamBuilderInterface $productStreamBuilder
  67.      */
  68.     public function __construct(
  69.         EntityRepositoryInterface $productGroupRepository,
  70.         SystemConfigService $systemConfigService,
  71.         EntityRepositoryInterface $productRepository,
  72.         ProductStreamBuilderInterface $productStreamBuilder,
  73.         Container $container
  74.     ) {
  75.         $this->productGroupRepository $productGroupRepository;
  76.         $this->systemConfigService $systemConfigService;
  77.         $this->productRepository $productRepository;
  78.         $this->productStreamBuilder $productStreamBuilder;
  79.         $this->productGroupId self::FALLBACK_PRODUCT_GROUP_ID;
  80.         $this->container $container;
  81.         $this->blacklistProductIds = [];
  82.     }
  83.     /**
  84.      * @return string[]
  85.      */
  86.     public static function getSubscribedEvents(): array
  87.     {
  88.         return [
  89.             NavigationLoadedEvent::class => 'onNavigationLoadedEvent',
  90.         ];
  91.     }
  92.     /**
  93.      * @param NavigationLoadedEvent $event
  94.      */
  95.     public function onNavigationLoadedEvent(NavigationLoadedEvent $event): void
  96.     {
  97.         $this->setProductGroup($event);
  98.         $hideCategories $this->systemConfigService->get('RhiemUserProducts.config.hideCategories');
  99.         if (!$hideCategories) {
  100.             return;
  101.         }
  102.         $event->getNavigation()->setTree($this->filterEmptyTreeCategories($event));
  103.     }
  104.     /**
  105.      * @param NavigationLoadedEvent $event
  106.      */
  107.     public function setProductGroup(NavigationLoadedEvent $event): void
  108.     {
  109.         $customer $event->getSalesChannelContext()->getCustomer();
  110.         if ($customer) {
  111.             /**
  112.              * @var ArrayStruct $foreignKeys
  113.              */
  114.             $foreignKeys $customer->getExtension('foreignKeys');
  115.             $productGroupId $foreignKeys->get('rhiemProductGroupId');
  116.             if ($productGroupId) {
  117.                 $this->productGroupId $productGroupId;
  118.                 $criteria = new Criteria([$this->productGroupId]);
  119.                 $productGroup $this->productGroupRepository->search(
  120.                     $criteria,
  121.                     $event->getContext()
  122.                 )->first();
  123.                 if ($productGroup->getActive()) {
  124.                     $this->productGroup $productGroup;
  125.                     $customer->addExtension('rhiemProductGroup'$this->productGroup);
  126.                 }
  127.             } else {
  128.                 $customerGroupId $customer->getGroupId();
  129.             }
  130.         } else {
  131.             $customerGroupId $event->getSalesChannelContext()->getCurrentCustomerGroup()->getId();
  132.         }
  133.         if(isset($customerGroupId)) {
  134.             $criteria = new Criteria();
  135.             $criteria->addFilter(new ContainsFilter('customerGroups'$customerGroupId));
  136.             
  137.             $productGroup $this->productGroupRepository->search(
  138.                 $criteria,
  139.                 $event->getContext()
  140.             )->first();
  141.             if($productGroup) {
  142.                 $this->productGroup $productGroup;
  143.             }
  144.         }
  145.         if (!isset($productGroup)) {
  146.             $criteria = new Criteria([$this->productGroupId]);
  147.             $productGroup $this->productGroupRepository->search(
  148.                 $criteria,
  149.                 $event->getContext()
  150.             )->first();
  151.             $this->productGroup $productGroup;
  152.         }
  153.     }
  154.     /**
  155.      * @param NavigationLoadedEvent $event
  156.      *
  157.      * @return array
  158.      */
  159.     public function filterEmptyTreeCategories(NavigationLoadedEvent $event): array
  160.     {
  161.         /**
  162.          * @var array $showCategoriesIds
  163.          */
  164.         $showCategoriesIds $this->systemConfigService->get('RhiemUserProducts.config.showCategories') ?: [];
  165.         $tree $event->getNavigation()->getTree();
  166.         /**
  167.          * @var Connection $connection
  168.          */
  169.         $connection $this->container->get('Doctrine\DBAL\Connection');
  170.         $this->setCategories($connection);
  171.         $this->setProductGroupCategoryIds($connection$event->getContext());
  172.         $tree $this->buildNewTree(
  173.             $tree,
  174.             $event->getContext(),
  175.             $event->getSalesChannelContext()->getSalesChannel()->getId(),
  176.             $showCategoriesIds
  177.         );
  178.         return $tree;
  179.     }
  180.     public function setProductGroupCategoryIds(Connection $connection$context): void
  181.     {
  182.         $criteria = new Criteria([self::FALLBACK_PRODUCT_GROUP_ID]);
  183.         $fallbackGroup $this->productGroupRepository->search(
  184.             $criteria,
  185.             $context
  186.         )->first();
  187.         if ($this->productGroup->getGroupMode() === "whitelist") {
  188.             $sql "SELECT p.category_tree FROM rhiem_userproducts_productgroup_product
  189.                 LEFT JOIN product p on rhiem_userproducts_productgroup_product.product_id = p.id AND rhiem_userproducts_productgroup_product.product_version_id = p.version_id AND (p.child_count IS NULL OR p.child_count = 0)
  190.                 WHERE rhiem_userproducts_productgroup_product.product_group_id = ? AND p.category_tree IS NOT NULL";
  191.         } elseif ($this->productGroup->getGroupMode() === "whitelist plus") {
  192.             if ($fallbackGroup->getGroupMode() === "whitelist") {
  193.                 $sql "SELECT p.category_tree FROM rhiem_userproducts_productgroup_product
  194.             LEFT JOIN product p on rhiem_userproducts_productgroup_product.product_id = p.id AND rhiem_userproducts_productgroup_product.product_version_id = p.version_id AND (p.child_count IS NULL OR p.child_count = 0)
  195.             WHERE (rhiem_userproducts_productgroup_product.product_group_id = ? AND p.category_tree IS NOT NULL)
  196.             OR (rhiem_userproducts_productgroup_product.product_group_id = ? AND p.category_tree IS NOT NULL)";
  197.             } else {
  198.                 $sql "SELECT DISTINCT category_tree from product WHERE 
  199.                 (id IN
  200.                 (SELECT product_id from rhiem_userproducts_productgroup_product 
  201.                  WHERE product_group_id = ?) 
  202.                  OR id NOT IN
  203.                  (SELECT product_id from rhiem_userproducts_productgroup_product 
  204.                  WHERE product_group_id = ?))
  205.                  AND (child_count IS NULL OR child_count = 0)";
  206.             }
  207.         } else {
  208.             $sql "SELECT DISTINCT category_tree from product WHERE id not in 
  209.                                              (SELECT product_id from rhiem_userproducts_productgroup_product WHERE product_group_id = ?) AND (child_count IS NULL OR child_count = 0)";
  210.         }
  211.         if ($this->productGroup->getGroupMode() === "whitelist plus") {
  212.             $result $connection->fetchAll($sql, [
  213.                 Uuid::fromHexToBytes($this->productGroupId),
  214.                 Uuid::fromHexToBytes(self::FALLBACK_PRODUCT_GROUP_ID)
  215.             ]);
  216.         } else {
  217.             $result $connection->fetchAll($sql, [Uuid::fromHexToBytes($this->productGroupId)]);
  218.         }
  219.         if (!$result) {
  220.             $this->productGroupCategoryIds = [];
  221.             return;
  222.         }
  223.         $result array_unique(
  224.             array_merge(
  225.                 ...array_map(function ($row) {
  226.                 if (!$row['category_tree']) {
  227.                     return [];
  228.                 }
  229.                 return \GuzzleHttp\json_decode($row['category_tree']);
  230.             }, $result)
  231.             )
  232.         );
  233.         $this->productGroupCategoryIds $result;
  234.     }
  235.     public function setCategories(Connection $connection): void
  236.     {
  237.         $sql "Select lower(hex(category.id)) as id, cp.type as cmsPageType, lower(hex(category.product_stream_id)) as productStreamId, category.product_assignment_type as productAssignmentType, lower(hex(category.product_stream_id)) as productStreamId from category
  238.                 LEFT JOIN cms_page cp on category.cms_page_id = cp.id;";
  239.         $result $connection->fetchAll($sql);
  240.         $categories = [];
  241.         foreach ($result as $row) {
  242.             $categories[$row['id']] = $row;
  243.         }
  244.         $this->categories $categories;
  245.     }
  246.     /**
  247.      * @param array $tree
  248.      * @param array $categoryIds
  249.      *
  250.      * @return array
  251.      */
  252.     protected function collectCategoryIds(array $tree, array $categoryIds): array
  253.     {
  254.         foreach ($tree as $treeItem) {
  255.             $categoryIds[] = $treeItem->getCategory()->getId();
  256.             $categoryIds $this->collectCategoryIds($treeItem->getChildren(), $categoryIds);
  257.         }
  258.         return array_unique($categoryIds);
  259.     }
  260.     /**
  261.      * @param array $tree
  262.      * @param Context $context
  263.      * @param string $salesChannelId
  264.      * @param array $showCategoriesIds
  265.      *
  266.      * @return array
  267.      */
  268.     protected function buildNewTree(
  269.         array $tree,
  270.         Context $context,
  271.         string $salesChannelId,
  272.         array $showCategoriesIds
  273.     ): array {
  274.         foreach ($tree as $key => $treeItem) {
  275.             $type $treeItem->getCategory()->getType();
  276.             $showCategory in_array($treeItem->getCategory()->getId(), $showCategoriesIds);
  277.             $children $treeItem->getChildren();
  278.             if (count($children) > 0) {
  279.                 $children $this->buildNewTree($children$context$salesChannelId$showCategoriesIds);
  280.                 if (!$showCategory && $type === "page" && count($children) == && !$this->hasProducts(
  281.                         $treeItem,
  282.                         $context,
  283.                         $salesChannelId
  284.                     )) {
  285.                     unset($tree[$key]);
  286.                     continue;
  287.                 }
  288.             } else {
  289.                 if (!$showCategory && $type === "page" && !$this->hasProducts($treeItem$context$salesChannelId)) {
  290.                     unset($tree[$key]);
  291.                     continue;
  292.                 }
  293.             }
  294.             $treeItem->setChildren($children);
  295.         }
  296.         return $tree;
  297.     }
  298.     /**
  299.      * @param TreeItem $treeItem
  300.      * @param Context $context
  301.      * @param string $salesChannelId
  302.      *
  303.      * @return bool
  304.      */
  305.     protected function hasProducts(TreeItem $treeItemContext $contextstring $salesChannelId): bool
  306.     {
  307.         $category $this->categories[$treeItem->getCategory()->getId()];
  308.         if ($category['cmsPageType'] && $category['cmsPageType'] !== 'product_list') {
  309.             return true;
  310.         }
  311.         if ($category['productAssignmentType'] === CategoryDefinition::PRODUCT_ASSIGNMENT_TYPE_PRODUCT_STREAM && isset($category['productStreamId'])) {
  312.             $criteria = new Criteria();
  313.             $criteria->addAssociation('product_groups');
  314.             if ($this->productGroup->getGroupMode() === "whitelist") {
  315.                 $criteria->addFilter(new EqualsFilter('product.product_groups.id'$this->productGroupId));
  316.             } elseif ($this->productGroup->getGroupMode() === "whitelist plus") {
  317.                 $criteria->addFilter(new EqualsFilter('product.product_groups.id'$this->productGroupId));
  318.                 $criteria->addFilter(new EqualsFilter('product.product_groups.id'self::FALLBACK_PRODUCT_GROUP_ID));
  319.             }
  320.             $criteria->addFilter(
  321.                 new ProductAvailableFilter($salesChannelIdProductVisibilityDefinition::VISIBILITY_ALL)
  322.             );
  323.             $filters $this->productStreamBuilder->buildFilters(
  324.                 $category['productStreamId'],
  325.                 $context
  326.             );
  327.             $criteria->addFilter(...$filters);
  328.             $productSearchIds $this->productRepository->searchIds($criteria$context);
  329.             if ($this->productGroup->getGroupMode() === "whitelist") {
  330.                 $total $productSearchIds->getTotal();
  331.                 return $total 0;
  332.             } else {
  333.                 if (!$this->blacklistProductIds) {
  334.                     /**
  335.                      * @var Connection $connection
  336.                      */
  337.                     $connection $this->container->get('Doctrine\DBAL\Connection');
  338.                     $sql "SELECT id FROM product LEFT JOIN rhiem_userproducts_productgroup_product rupp on product.id = rupp.product_id and product.version_id = rupp.product_version_id WHERE rupp.product_group_id = ?";
  339.                     $result $connection->fetchAll($sql, [Uuid::fromHexToBytes($this->productGroupId)]);
  340.                     $this->blacklistProductIds array_map(function ($row) {
  341.                         return $row['id'];
  342.                     }, $result);
  343.                 }
  344.                 if ($productSearchIds->getTotal() > count($this->blacklistProductIds)) {
  345.                     return true;
  346.                 }
  347.                 foreach ($productSearchIds->getIds() as $id) {
  348.                     if (!in_array($id$this->blacklistProductIds)) {
  349.                         return true;
  350.                     }
  351.                 }
  352.                 return false;
  353.             }
  354.         } else {
  355.             return in_array($treeItem->getCategory()->getId(), $this->productGroupCategoryIds);
  356.         }
  357.     }
  358. }