platform/src/Core/Checkout/Promotion/Subscriber/Storefront/StorefrontCartSubscriber.php line 44

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Promotion\Subscriber\Storefront;
  3. use Shopware\Core\Checkout\Cart\Cart;
  4. use Shopware\Core\Checkout\Cart\Event\BeforeLineItemAddedEvent;
  5. use Shopware\Core\Checkout\Cart\Event\BeforeLineItemRemovedEvent;
  6. use Shopware\Core\Checkout\Cart\Event\CheckoutOrderPlacedEvent;
  7. use Shopware\Core\Checkout\Cart\Exception\LineItemNotFoundException;
  8. use Shopware\Core\Checkout\Cart\Exception\LineItemNotRemovableException;
  9. use Shopware\Core\Checkout\Cart\Exception\PayloadKeyNotFoundException;
  10. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  11. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  12. use Shopware\Core\Checkout\Promotion\Aggregate\PromotionDiscount\PromotionDiscountEntity;
  13. use Shopware\Core\Checkout\Promotion\Cart\Extension\CartExtension;
  14. use Shopware\Core\Checkout\Promotion\Cart\PromotionProcessor;
  15. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. use Symfony\Component\HttpFoundation\RequestStack;
  18. class StorefrontCartSubscriber implements EventSubscriberInterface
  19. {
  20.     public const SESSION_KEY_PROMOTION_CODES 'cart-promotion-codes';
  21.     private CartService $cartService;
  22.     private RequestStack $requestStack;
  23.     public function __construct(CartService $cartServiceRequestStack $requestStack)
  24.     {
  25.         $this->cartService $cartService;
  26.         $this->requestStack $requestStack;
  27.     }
  28.     public static function getSubscribedEvents(): array
  29.     {
  30.         return [
  31.             BeforeLineItemAddedEvent::class => 'onLineItemAdded',
  32.             BeforeLineItemRemovedEvent::class => 'onLineItemRemoved',
  33.             CheckoutOrderPlacedEvent::class => 'resetCodes',
  34.         ];
  35.     }
  36.     public function resetCodes(): void
  37.     {
  38.         $mainRequest $this->requestStack->getMainRequest();
  39.         if ($mainRequest === null) {
  40.             return;
  41.         }
  42.         if (!$mainRequest->hasSession()) {
  43.             return;
  44.         }
  45.         $mainRequest->getSession()->set(self::SESSION_KEY_PROMOTION_CODES, []);
  46.     }
  47.     /**
  48.      * This function is called whenever a new line item has been
  49.      * added to the cart from within the controllers.
  50.      * We verify if we have a placeholder line item for a promotion
  51.      * and add that code to our extension list.
  52.      */
  53.     public function onLineItemAdded(BeforeLineItemAddedEvent $event): void
  54.     {
  55.         if ($event->getLineItem()->getType() === PromotionProcessor::LINE_ITEM_TYPE) {
  56.             $code $event->getLineItem()->getReferencedId();
  57.             if ($code !== null && $code !== '') {
  58.                 $this->addCode($code$event->getCart());
  59.             }
  60.         }
  61.     }
  62.     /**
  63.      * This function is called whenever a line item is being removed
  64.      * from the cart from within a controller.
  65.      * We verify if it is a promotion item, and also remove that
  66.      * code from our extension, if existing.
  67.      */
  68.     public function onLineItemRemoved(BeforeLineItemRemovedEvent $event): void
  69.     {
  70.         $cart $event->getCart();
  71.         if ($event->getLineItem()->getType() !== PromotionProcessor::LINE_ITEM_TYPE) {
  72.             return;
  73.         }
  74.         $lineItem $event->getLineItem();
  75.         $code $lineItem->getReferencedId();
  76.         if (!empty($code)) {
  77.             // promotion with code
  78.             $this->checkFixedDiscountItems($cart$lineItem);
  79.             //remove other discounts of the promotion that should be deleted
  80.             $this->removeOtherDiscountsOfPromotion($cart$lineItem$event->getSalesChannelContext());
  81.             $this->removeCode($code$cart);
  82.             return;
  83.         }
  84.         // the user wants to remove an automatic added
  85.         // promotions, so lets do this
  86.         if ($lineItem->hasPayloadValue('promotionId')) {
  87.             $promotionId = (string) $lineItem->getPayloadValue('promotionId');
  88.             $this->blockPromotion($promotionId$cart);
  89.         }
  90.     }
  91.     /**
  92.      * @throws LineItemNotFoundException
  93.      * @throws LineItemNotRemovableException
  94.      * @throws PayloadKeyNotFoundException
  95.      */
  96.     private function checkFixedDiscountItems(Cart $cartLineItem $lineItem): void
  97.     {
  98.         $lineItems $cart->getLineItems()->filterType(PromotionProcessor::LINE_ITEM_TYPE);
  99.         if ($lineItems->count() < 1) {
  100.             return;
  101.         }
  102.         if (!$lineItem->hasPayloadValue('discountType')) {
  103.             return;
  104.         }
  105.         if ($lineItem->getPayloadValue('discountType') !== PromotionDiscountEntity::TYPE_FIXED_UNIT) {
  106.             return;
  107.         }
  108.         if (!$lineItem->hasPayloadValue('discountId')) {
  109.             return;
  110.         }
  111.         $discountId $lineItem->getPayloadValue('discountId');
  112.         $removeThisDiscounts $lineItems->filter(static function (LineItem $lineItem) use ($discountId) {
  113.             return $lineItem->hasPayloadValue('discountId') && $lineItem->getPayloadValue('discountId') === $discountId;
  114.         });
  115.         foreach ($removeThisDiscounts as $discountItem) {
  116.             $cart->remove($discountItem->getId());
  117.         }
  118.     }
  119.     private function removeOtherDiscountsOfPromotion(Cart $cartLineItem $lineItemSalesChannelContext $context): void
  120.     {
  121.         // ge all promotions from cart
  122.         $lineItems $cart->getLineItems()->filterType(PromotionProcessor::LINE_ITEM_TYPE);
  123.         if ($lineItems->count() < 1) {
  124.             return;
  125.         }
  126.         //filter them by the promotion which discounts should be deleted
  127.         $lineItems $lineItems->filter(function (LineItem $promotionLineItem) use ($lineItem) {
  128.             return $promotionLineItem->getPayloadValue('promotionId') === $lineItem->getPayloadValue('promotionId');
  129.         });
  130.         if ($lineItems->count() < 1) {
  131.             return;
  132.         }
  133.         $promotionLineItem $lineItems->first();
  134.         if ($promotionLineItem instanceof LineItem) {
  135.             // this is recursive because we are listening on LineItemRemovedEvent, it will stop if there
  136.             // are no discounts in the cart, that belong to the promotion that should be deleted
  137.             $this->cartService->remove($cart$promotionLineItem->getId(), $context);
  138.         }
  139.     }
  140.     private function addCode(string $codeCart $cart): void
  141.     {
  142.         $extension $this->getExtension($cart);
  143.         $extension->addCode($code);
  144.         $cart->addExtension(CartExtension::KEY$extension);
  145.     }
  146.     private function removeCode(string $codeCart $cart): void
  147.     {
  148.         $extension $this->getExtension($cart);
  149.         $extension->removeCode($code);
  150.         $cart->addExtension(CartExtension::KEY$extension);
  151.     }
  152.     private function blockPromotion(string $idCart $cart): void
  153.     {
  154.         $extension $this->getExtension($cart);
  155.         $extension->blockPromotion($id);
  156.         $cart->addExtension(CartExtension::KEY$extension);
  157.     }
  158.     private function getExtension(Cart $cart): CartExtension
  159.     {
  160.         if (!$cart->hasExtension(CartExtension::KEY)) {
  161.             $cart->addExtension(CartExtension::KEY, new CartExtension());
  162.         }
  163.         /** @var CartExtension $extension */
  164.         $extension $cart->getExtension(CartExtension::KEY);
  165.         return $extension;
  166.     }
  167. }