custom/static-plugins/EfbStorefront/Subscriber/CartPageSubscriber.php line 44

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Uandi\EFB\Storefront\Subscriber;
  3. use Shopware\Core\Checkout\Cart\Cart;
  4. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  5. use Shopware\Core\Content\Product\Aggregate\ProductCrossSelling\ProductCrossSellingCollection;
  6. use Shopware\Core\Content\Product\ProductEntity;
  7. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  9. use Shopware\Core\Framework\Struct\ArrayStruct;
  10. use Shopware\Core\Framework\Struct\Struct;
  11. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  12. use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedEvent;
  13. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  14. use Uandi\EFB\Storefront\Service\CrossSellingLoader;
  15. use Uandi\EFB\Storefront\Trait\DecodeStockInformation;
  16. class CartPageSubscriber implements EventSubscriberInterface
  17. {
  18.     use DecodeStockInformation;
  19.     private const CROSS_SELLING_LIMIT 5;
  20.     private const MAX_ITEMS_FOR_CROSS_SELLING 20;
  21.     private CrossSellingLoader $crossSellingLoader;
  22.     private EntityRepository $productRepository;
  23.     public static function getSubscribedEvents(): array
  24.     {
  25.         return [
  26.             CheckoutCartPageLoadedEvent::class => 'onCartPageLoadedEvent',
  27.         ];
  28.     }
  29.     public function __construct(
  30.         CrossSellingLoader $crossSellingLoader,
  31.         EntityRepository $productRepository
  32.     ) {
  33.         $this->crossSellingLoader $crossSellingLoader;
  34.         $this->productRepository $productRepository;
  35.     }
  36.     public function onCartPageLoadedEvent(CheckoutCartPageLoadedEvent $event)
  37.     {
  38.         $cart $event->getPage()->getCart();
  39.         $context $event->getSalesChannelContext();
  40.         $productIds $this->getProductIds($cart);
  41.         if (empty($productIds)) {
  42.             return;
  43.         }
  44.         $cart->addExtensions([
  45.             'crossSelling' => $this->buildCrossSelling($productIds$context),
  46.             'stockInfo' => $this->fetchStockInformation($productIds$context),
  47.         ]);
  48.     }
  49.     private function fetchStockInformation(array $productIdsSalesChannelContext $context): ?Struct
  50.     {
  51.         $criteria = new Criteria($productIds);
  52.         $products $this->productRepository->search($criteria$context->getContext());
  53.         return new ArrayStruct($products->map(function (ProductEntity $productEntity) use ($context) {
  54.             return $this->decodeStockInfo($productEntity$context);
  55.         }));
  56.     }
  57.     private function buildCrossSelling(array $productIdsSalesChannelContext $context): ?Struct
  58.     {
  59.         if (count($productIds) > static::MAX_ITEMS_FOR_CROSS_SELLING) {
  60.             return null;
  61.         }
  62.         $crossSellingCollection $this->crossSellingLoader->fetchAssignedCrossSellings(
  63.             $productIds,
  64.             $context->getContext()
  65.         );
  66.         if (empty($crossSellingCollection->getElements())) {
  67.             return null;
  68.         }
  69.         $crossSellingCollection $this->filterForUniqueName($crossSellingCollection);
  70.         if ($crossSellingCollection->count() > static::CROSS_SELLING_LIMIT) {
  71.             $crossSellingCollection = new ProductCrossSellingCollection(
  72.                 $this->getRandomCrossSelling($crossSellingCollection)
  73.             );
  74.         }
  75.         return $this->crossSellingLoader->loadCrossSellings(
  76.             $crossSellingCollection,
  77.             $context
  78.         );
  79.     }
  80.     private function getProductIds(Cart $cart): array
  81.     {
  82.         $products $cart->getLineItems()->filterFlatByType(LineItem::PRODUCT_LINE_ITEM_TYPE);
  83.         return array_map(function (LineItem $lineItem) {
  84.             return $lineItem->getReferencedId();
  85.         }, $products);
  86.     }
  87.     private function filterForUniqueName(
  88.         ProductCrossSellingCollection $crossSellingCollection
  89.     ): ProductCrossSellingCollection {
  90.         foreach ($crossSellingCollection as $crossSelling) {
  91.             $name $crossSelling->getName();
  92.             if (false === array_key_exists($name$filtered ?? [])) {
  93.                 $filtered[$name] = $crossSelling;
  94.             }
  95.         }
  96.         return new ProductCrossSellingCollection($filtered ?? []);
  97.     }
  98.     public function getRandomCrossSelling(ProductCrossSellingCollection $crossSellingCollection): array
  99.     {
  100.         $result = [];
  101.         $crossSellingElements $crossSellingCollection->getElements();
  102.         $randomValues array_rand($crossSellingElements, static::CROSS_SELLING_LIMIT);
  103.         foreach ($randomValues as $randomValue) {
  104.             $result[] = $crossSellingElements[$randomValue];
  105.         }
  106.         return $result;
  107.     }
  108. }