custom/plugins/RhiemUserProducts/src/Subscriber/ProductCriteriaEvents.php line 111

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\Uuid\Uuid;
  6. use Shopware\Core\Content\Product\ProductEntity;
  7. use Rhiem\RhiemUserProducts\Services\ProductGroupService;
  8. use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
  9. use Shopware\Storefront\Page\Product\ProductPageCriteriaEvent;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. use Shopware\Core\Content\Product\Events\ProductSearchCriteriaEvent;
  12. use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
  13. use Shopware\Core\Content\Product\Events\ProductSuggestCriteriaEvent;
  14. use Shopware\Core\Content\Product\Events\ProductCrossSellingsLoadedEvent;
  15. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchedEvent;
  18. use Shopware\Core\Content\Product\Events\ProductCrossSellingIdsCriteriaEvent;
  19. use Shopware\Core\Content\Product\ProductDefinition;
  20. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  22. use Shopware\Storefront\Page\Product\QuickView\MinimalQuickViewPageCriteriaEvent;
  23. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionCollection;
  24. class ProductCriteriaEvents implements EventSubscriberInterface
  25. {
  26.     const GROUP_MODE_WHITE 0;
  27.     const GROUP_MODE_BLACK 1;
  28.     /**
  29.      * @var SalesChannelRepositoryInterface
  30.      */
  31.     private SalesChannelRepositoryInterface $productRepository;
  32.     /**
  33.      * @var ProductGroupService
  34.      */
  35.     private ProductGroupService $productGroupService;
  36.     /**
  37.      * @var Connection
  38.      */
  39.     private $connection;
  40.     public function __construct(
  41.         SalesChannelRepositoryInterface $productRepository,
  42.         ProductGroupService $productGroupService,
  43.         Connection $connection
  44.     ) {
  45.         $this->productRepository $productRepository;
  46.         $this->productGroupService $productGroupService;
  47.         $this->connection $connection;
  48.     }
  49.     public static function getSubscribedEvents()
  50.     {
  51.         return [
  52.             ProductListingCriteriaEvent::class => 'productGroupCriteria',
  53.             ProductPageCriteriaEvent::class => 'productGroupCriteria',
  54.             MinimalQuickViewPageCriteriaEvent::class => 'productGroupCriteria',
  55.             ProductSearchCriteriaEvent::class => 'productGroupCriteria',
  56.             ProductSuggestCriteriaEvent::class => 'productGroupCriteria',
  57.             ProductCrossSellingIdsCriteriaEvent::class => 'productGroupCriteria',
  58.             ProductCrossSellingsLoadedEvent::class => 'onCrossSellingLoadedEvent',
  59.             ProductPageLoadedEvent::class => 'onProductPageLoadedEvent'
  60.         ];
  61.     }
  62.     /**
  63.      * @param ProductListingCriteriaEvent $event
  64.      */
  65.     public function productGroupCriteria($event): void
  66.     {
  67.         $context $event->getContext();
  68.         if(method_exists($event,'getRequest')) {
  69.             $navigationId $event->getRequest()->get('navigationId');
  70.             $productGroup $this->productGroupService->getProductGroup($event->getSalesChannelContext(), $navigationId);
  71.         } else {
  72.             $productGroup $this->productGroupService->getProductGroup($event->getSalesChannelContext());
  73.         }
  74.         
  75.         $productIds $this->productGroupService->getProductIds($productGroup);
  76.         $criteria $event->getCriteria();
  77.         $this->filterCriteria($criteria$productIds$productGroup$context);
  78.     }
  79.     public function onCrossSellingLoadedEvent(ProductCrossSellingsLoadedEvent $event)
  80.     {
  81.         $crossSellings $event->getCrossSellings();
  82.         $context $event->getContext();
  83.         $productGroup $this->productGroupService->getProductGroup($event->getSalesChannelContext());
  84.         $productIds $this->productGroupService->getProductIds($productGroup);
  85.         foreach ($crossSellings as $crossSelling) {
  86.             $products $crossSelling->getProducts();
  87.             $filteredProducts $products->filter(function (ProductEntity $product) use ($productIds$productGroup$context) {
  88.                 return $this->filterByProductId($product->getId(), $productIds$productGroup$context);
  89.             });
  90.             $crossSelling->setProducts($filteredProducts);
  91.         }
  92.     }
  93.     public function onProductPageLoadedEvent(ProductPageLoadedEvent $event)
  94.     {
  95.         $configuratorSettings $event->getPage()->getConfiguratorSettings();
  96.         $context $event->getContext();
  97.         $productGroup $this->productGroupService->getProductGroup($event->getSalesChannelContext());
  98.         $productIds $this->productGroupService->getProductIds($productGroup);
  99.         if ($productGroup->getGroupMode() === 'whitelist plus') {
  100.             $fallBackGroup $this->productGroupService->getFallbackGroup($context);
  101.             $fallBackProductIds $this->productGroupService->getProductIds($fallBackGroup);
  102.             if ($fallBackGroup->getGroupMode() === 'whitelist') {
  103.                 $productIds array_unique(array_merge($fallBackProductIds$productIds));
  104.             } else {
  105.                 $productIds array_diff($fallBackProductIds$productIds);
  106.             }
  107.         }
  108.         $groupMode self::GROUP_MODE_BLACK;
  109.         if ($productGroup->getGroupMode() === 'whitelist' || ($productGroup->getGroupMode() === 'whitelist plus' && $fallBackGroup->getGroupMode() === 'whitelist')) {
  110.             $groupMode self::GROUP_MODE_WHITE;
  111.         }
  112.         $mainProduct $event->getPage()->getProduct();
  113.         $mainOptions $this->getSortedOptions($mainProduct);
  114.         if ($mainProduct->getParentId()) {
  115.             $parentId $mainProduct->getParentId();
  116.         } else {
  117.             $parentId $mainProduct->getId();
  118.         }
  119.         list($combinations$optionIds) = $this->getActiveOptions($parentId$productIds$groupMode);
  120.         foreach ($configuratorSettings as $configuratorSetting) {
  121.             $allowedOptions = new PropertyGroupOptionCollection();
  122.             foreach ($configuratorSetting->getOptions()->getElements() as $option) {
  123.                 if (in_array($option->getId(), $optionIds)) {
  124.                     if (in_array($option->getId(), $mainProduct->getOptionIds())) {
  125.                         $allowedOptions->add($option);
  126.                         continue;
  127.                     }
  128.                     $option->setCombinable(false);
  129.                     $compareCombination $mainOptions;
  130.                     $compareCombination[$option->getGroupId()] = $option->getId();
  131.                     $compareCombination array_values($compareCombination);
  132.                     foreach ($combinations as $combination) {
  133.                         $combinable true;
  134.                         foreach ($compareCombination as $optionValue) {
  135.                             if (!in_array($optionValue$combination)) {
  136.                                 $combinable false;
  137.                                 break;
  138.                             }
  139.                         }
  140.                         if ($combinable) {
  141.                             $option->setCombinable(true);
  142.                             break;
  143.                         }
  144.                     }
  145.                     $allowedOptions->add($option);
  146.                 }
  147.             }
  148.             $configuratorSetting->setOptions($allowedOptions);
  149.         }
  150.     }
  151.     private function filterCriteria($criteria$productIds$productGroup$context): void
  152.     {
  153.         if ($productGroup->getGroupMode() === 'whitelist') {
  154.             $criteria->addFilter(new EqualsAnyFilter('id'$productIds));
  155.         } elseif ($productGroup->getGroupMode() === 'whitelist plus') {
  156.             $fallBackGroup $this->productGroupService->getFallbackGroup($context);
  157.             $fallBackProductIds $this->productGroupService->getProductIds($fallBackGroup);
  158.             if ($fallBackGroup->getGroupMode() === 'whitelist') {
  159.                 $mergedProductIds array_unique(array_merge($fallBackProductIds$productIds));
  160.                 $criteria->addFilter(new EqualsAnyFilter('id'$mergedProductIds));
  161.             } else {
  162.                 $diffedProductIds array_diff($fallBackProductIds$productIds);
  163.                 if (count($diffedProductIds) > 0) {
  164.                     $criteria->addFilter(
  165.                         new NotFilter(
  166.                             NotFilter::CONNECTION_OR,
  167.                             [new EqualsAnyFilter('id'$diffedProductIds)]
  168.                         )
  169.                     );
  170.                 }
  171.             }
  172.         } else {
  173.             if (count($productIds) > 0) {
  174.                 $criteria->addFilter(
  175.                     new NotFilter(
  176.                         NotFilter::CONNECTION_OR,
  177.                         [new EqualsAnyFilter('id'$productIds)]
  178.                     )
  179.                 );
  180.             }
  181.         }
  182.     }
  183.     private function filterByProductId($productId$productIds$productGroup$context): bool
  184.     {
  185.         if ($productGroup->getGroupMode() === 'whitelist') {
  186.             if (in_array($productId$productIds)) {
  187.                 return true;
  188.             }
  189.             return false;
  190.         } else if ($productGroup->getGroupMode() === 'whitelist plus') {
  191.             $fallBackGroup $this->productGroupService->getFallbackGroup($context);
  192.             $fallBackProductIds $this->productGroupService->getProductIds($fallBackGroup);
  193.             if ($fallBackGroup->getGroupMode() === 'whitelist') {
  194.                 $mergedProductIds array_unique(array_merge($fallBackProductIds$productIds));
  195.                 if (in_array($productId$mergedProductIds)) {
  196.                     return true;
  197.                 }
  198.                 return false;
  199.             } else {
  200.                 $diffedProductIds array_diff($fallBackProductIds$productIds);
  201.                 if (in_array($productId$diffedProductIds)) {
  202.                     return false;
  203.                 }
  204.                 return true;
  205.             }
  206.         } else {
  207.             if (in_array($productId$productIds)) {
  208.                 return false;
  209.             }
  210.             return true;
  211.         }
  212.     }
  213.     private function getActiveOptions($parentId$productIds$groupMode)
  214.     {
  215.         $inString '';
  216.         if ($groupMode == self::GROUP_MODE_BLACK) {
  217.             $inString 'NOT';
  218.             if (count($productIds) == 0) {
  219.                 $productIds = [UUID::randomHex()];
  220.             }
  221.         }
  222.         $query = <<<SQL
  223.                         SELECT option_ids
  224.                         FROM product 
  225.                         WHERE parent_id=?
  226.                         AND id $inString IN(?);
  227.                 SQL;
  228.         $result $this->connection->fetchFirstColumn(
  229.             $query,
  230.             [Uuid::fromHexToBytes($parentId), UUID::fromHexToBytesList($productIds)],
  231.             [\PDO::PARAM_STRConnection::PARAM_STR_ARRAY]
  232.         );
  233.         $combinations array_map(function (String $options) {
  234.             return json_decode($options);
  235.         }, $result);
  236.         $optionIds array_unique(array_merge(...$combinations));
  237.         return [$combinations$optionIds];
  238.     }
  239.     private function getSortedOptions(SalesChannelProductEntity $product)
  240.     {
  241.         $result = [];
  242.         foreach ($product->getOptions() as $option) {
  243.             $result[$option->getGroup()->getId()] = $option->getId();
  244.         }
  245.         return $result;
  246.     }
  247. }