commerce_license-8.x-2.x-dev/src/EventSubscriber/LicenseMultiplesCartEventSubscriber.php
src/EventSubscriber/LicenseMultiplesCartEventSubscriber.php
<?php namespace Drupal\commerce_license\EventSubscriber; use Drupal\commerce_cart\Event\CartEntityAddEvent; use Drupal\commerce_cart\Event\CartEvents; use Drupal\commerce_cart\Event\CartOrderItemUpdateEvent; use Drupal\commerce_order\Entity\OrderItemInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerTrait; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Ensures only 1 of each license may be added to the cart. * * We need to act on the cart event, as need to change the item quantity before * \Drupal\commerce_cart\EventSubscriber\CartEventSubscriber can show a message * to the user about adding the item to the cart. * * @see \Drupal\commerce_license\LicenseOrderProcessorMultiples */ class LicenseMultiplesCartEventSubscriber implements EventSubscriberInterface { use MessengerTrait; use StringTranslationTrait; /** * Constructs a new LicenseMultiplesCartEventSubscriber object. * * @param \Drupal\Core\Messenger\MessengerInterface $messenger * The messenger. */ public function __construct(MessengerInterface $messenger) { $this->setMessenger($messenger); } /** * {@inheritdoc} */ public static function getSubscribedEvents() { return [ // These need to run before // \Drupal\commerce_cart\EventSubscriber\CartEventSubscriber. // Also expects the patch at // https://www.drupal.org/project/commerce/issues/2930979 // so that CartEventSubscriber doesn't show its message when we prevent // an item from being added. CartEvents::CART_ENTITY_ADD => ['onCartEntityAdd', 100], CartEvents::CART_ORDER_ITEM_UPDATE => ['onCartItemUpdate', 100], ]; } /** * Enforces the maximum count when a product is added to the cart. * * Note that this event is triggered when adding a product to the cart from * the add to cart form when an item for this product is already in the cart. * This is treated as an entity add, even though technically it's an order * item that is being updated. * * @param \Drupal\commerce_cart\Event\CartEntityAddEvent $event * The cart event. * * @throws \Drupal\Core\Entity\EntityStorageException */ public function onCartEntityAdd(CartEntityAddEvent $event): void { $order_item = $event->getOrderItem(); // Only act if the order item has a license reference field. if (!$order_item->hasField('license')) { return; } // @todo Allow license type plugins to respond here, as for types that // collect user data in the checkout form, the same product variation can // result in different licenses. // Only act if the order item quantity is greater than 1. if ($order_item->getQuantity() == 1) { return; } // Force the quantity back to 1. $this->forceOrderItemQuantity($order_item); $this->messenger()->addError($this->t('You may only have one of @product-label in <a href="@cart-url">your cart</a>.', [ '@product-label' => $order_item->getPurchasedEntity()->label(), '@cart-url' => Url::fromRoute('commerce_cart.page')->toString(), ])); // Since we have reverted the quantity, we will stop propagation, so that // add to cart messages are not logged nor displayed. $event->stopPropagation(); } /** * Enforces the maximum count when an item is updated. * * @param \Drupal\commerce_cart\Event\CartOrderItemUpdateEvent $event * The cart event. * * @throws \Drupal\Core\Entity\EntityStorageException */ public function onCartItemUpdate(CartOrderItemUpdateEvent $event): void { $order_item = $event->getOrderItem(); // Only act if the order item has a license reference field. if (!$order_item->hasField('license')) { return; } // @todo Allow license type plugins to respond here, as for types that // collect user data in the checkout form, the same product variation can // result in different licenses. // Only act if the order item quantity is greater than 1. if ($order_item->getQuantity() == 1) { return; } // Force the quantity back to 1. $this->forceOrderItemQuantity($order_item); // Don't show a link to the cart as the user will typically be on the cart // page. $this->messenger()->addError($this->t('You may only have one of @product-label in your cart.', [ '@product-label' => $order_item->getPurchasedEntity()->label(), ])); } /** * Forces the quantity of an order item to 1, and saves it. * * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item * The order item. * * @throws \Drupal\Core\Entity\EntityStorageException */ protected function forceOrderItemQuantity(OrderItemInterface $order_item): void { // Force the quantity back to 1. $order_item->setQuantity(1); // Save the order item again. $order_item->save(); } }