custom/plugins/HemoCustomerGroupRestriction/src/Subscriber/ProductVisibilitySubscriber.php line 54

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Hemo\CustomerGroupRestriction\Subscriber;
  3. use Hemo\CustomerGroupRestriction\Service\ProductRestrictionLoader;
  4. use Shopware\Core\Content\Category\Event\CategoryRouteCacheKeyEvent;
  5. use Shopware\Core\Content\LandingPage\Event\LandingPageRouteCacheKeyEvent;
  6. use Shopware\Core\Content\Product\Events\CrossSellingRouteCacheKeyEvent;
  7. use Shopware\Core\Content\Product\Events\ProductDetailRouteCacheKeyEvent;
  8. use Shopware\Core\Content\Product\Events\ProductListingRouteCacheKeyEvent;
  9. use Shopware\Core\Content\Product\Events\ProductSearchRouteCacheKeyEvent;
  10. use Shopware\Core\Content\Product\Events\ProductSuggestRouteCacheKeyEvent;
  11. use Shopware\Core\Framework\Adapter\Cache\StoreApiRouteCacheKeyEvent;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
  16. use Shopware\Core\System\SalesChannel\Event\SalesChannelProcessCriteriaEvent;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. /**
  19.  * Hides restricted products from every sales channel product read (listing, search, suggest,
  20.  * detail page, CMS sliders, cross selling, Store API) for customers outside the allowed groups.
  21.  */
  22. final class ProductVisibilitySubscriber implements EventSubscriberInterface
  23. {
  24.     /**
  25.      * Criteria title used by Shopware's ProductGateway when loading cart products. The cart
  26.      * processor handles that case itself and reports a meaningful error to the customer.
  27.      */
  28.     private const CART_CRITERIA_TITLE 'cart::products';
  29.     private ProductRestrictionLoader $loader;
  30.     public function __construct(ProductRestrictionLoader $loader)
  31.     {
  32.         $this->loader $loader;
  33.     }
  34.     public static function getSubscribedEvents(): array
  35.     {
  36.         return [
  37.             'sales_channel.product.process.criteria' => 'onProcessCriteria',
  38.             ProductListingRouteCacheKeyEvent::class => 'onCacheKey',
  39.             ProductSearchRouteCacheKeyEvent::class => 'onCacheKey',
  40.             ProductSuggestRouteCacheKeyEvent::class => 'onCacheKey',
  41.             ProductDetailRouteCacheKeyEvent::class => 'onCacheKey',
  42.             CrossSellingRouteCacheKeyEvent::class => 'onCacheKey',
  43.             CategoryRouteCacheKeyEvent::class => 'onCacheKey',
  44.             LandingPageRouteCacheKeyEvent::class => 'onCacheKey',
  45.         ];
  46.     }
  47.     public function onProcessCriteria(SalesChannelProcessCriteriaEvent $event): void
  48.     {
  49.         $criteria $event->getCriteria();
  50.         if ($criteria->getTitle() === self::CART_CRITERIA_TITLE) {
  51.             return;
  52.         }
  53.         $customerGroupId $event->getSalesChannelContext()->getCurrentCustomerGroup()->getId();
  54.         $hiddenIds $this->loader->getHiddenProductIds($customerGroupId);
  55.         if ($hiddenIds === []) {
  56.             return;
  57.         }
  58.         // A variant without a restriction of its own inherits the parent's restriction.
  59.         $parentVisibility = [
  60.             new EqualsFilter('product.parentId'null),
  61.             new NotFilter(NotFilter::CONNECTION_AND, [new EqualsAnyFilter('product.parentId'$hiddenIds)]),
  62.         ];
  63.         $ownRestriction $this->loader->getRestrictedProductIds();
  64.         if ($ownRestriction !== []) {
  65.             $parentVisibility[] = new EqualsAnyFilter('product.id'$ownRestriction);
  66.         }
  67.         $criteria->addFilter(new MultiFilter(MultiFilter::CONNECTION_AND, [
  68.             new NotFilter(NotFilter::CONNECTION_AND, [new EqualsAnyFilter('product.id'$hiddenIds)]),
  69.             new MultiFilter(MultiFilter::CONNECTION_OR$parentVisibility),
  70.         ]));
  71.     }
  72.     /**
  73.      * The cached Store API routes build their cache key before the criteria is processed, so the
  74.      * customer group has to become part of the key explicitly.
  75.      */
  76.     public function onCacheKey(StoreApiRouteCacheKeyEvent $event): void
  77.     {
  78.         $event->addPart($event->getContext()->getCurrentCustomerGroup()->getId());
  79.     }
  80. }