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

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