vendor/shopware/core/Checkout/Promotion/Subscriber/Storefront/StorefrontCartSubscriber.php line 59

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