You are here

class OrderSubscriber in Commerce License 8.2

Changes a license's state in sync with an order's workflow.

Hierarchy

  • class \Drupal\commerce_license\EventSubscriber\OrderSubscriber implements \Symfony\Component\EventDispatcher\EventSubscriberInterface

Expanded class hierarchy of OrderSubscriber

1 string reference to 'OrderSubscriber'
commerce_license.services.yml in ./commerce_license.services.yml
commerce_license.services.yml
1 service uses OrderSubscriber
commerce_license.order_subscriber in ./commerce_license.services.yml
Drupal\commerce_license\EventSubscriber\OrderSubscriber

File

src/EventSubscriber/OrderSubscriber.php, line 16

Namespace

Drupal\commerce_license\EventSubscriber
View source
class OrderSubscriber implements EventSubscriberInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Constructs a new OrderSubscriber object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      OrderEvents::ORDER_PAID => 'onPaid',
      'commerce_order.place.pre_transition' => [
        'onPlace',
        100,
      ],
      // Event for reaching the 'canceled' order state.
      'commerce_order.cancel.post_transition' => [
        'onCancel',
        -100,
      ],
    ];
  }

  /**
   * Activates the licenses on order paid.
   *
   * @param \Drupal\commerce_order\Event\OrderEvent $event
   *   The order event.
   */
  public function onPaid(OrderEvent $event) {
    $order = $event
      ->getOrder();
    $licensable_order_items = $this
      ->getLicensableOrderItems($order);
    foreach ($licensable_order_items as $order_item) {

      /** @var \Drupal\commerce_license\Entity\LicenseInterface $license */
      $license = $order_item
        ->get('license')->entity;

      // We don't need to do anything if there is already an active license
      // referenced by this order item.
      if ($license && $license
        ->getState()
        ->getId() === 'active') {
        continue;
      }
      if (!$license) {
        $license = $this
          ->createLicenseFromOrderItem($order_item);
      }
      $license
        ->set('state', 'active');
      $license
        ->save();
    }
  }

  /**
   * Reacts to an order being placed.
   *
   * Creates the licenses for licensable order items, and optionally activate
   * them if configured to do so at the product variation type level.
   *
   * @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
   *   The event we subscribed to.
   */
  public function onPlace(WorkflowTransitionEvent $event) {

    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $event
      ->getEntity();
    $licensable_order_items = $this
      ->getLicensableOrderItems($order);
    $product_variation_type_storage = $this->entityTypeManager
      ->getStorage('commerce_product_variation_type');
    foreach ($licensable_order_items as $order_item) {
      $license = $order_item
        ->get('license')->entity;

      // We don't need to do anything if there is already an active license
      // referenced by this order item.
      if ($license && $license
        ->getState()
        ->getId() === 'active') {
        continue;
      }
      if (!$license) {
        $license = $this
          ->createLicenseFromOrderItem($order_item);
      }
      $purchased_entity = $order_item
        ->getPurchasedEntity();
      $product_variation_type = $product_variation_type_storage
        ->load($purchased_entity
        ->bundle());
      $activate_on_place = $product_variation_type
        ->getThirdPartySetting('commerce_license', 'activate_on_place');

      // License activation shouldn't happen when the order is placed, skip
      // to the next order item.
      if (!$activate_on_place) {
        continue;
      }
      $license
        ->set('state', 'active');
      $license
        ->save();
    }
  }

  /**
   * Reacts to an order being cancelled.
   *
   * @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
   *   The event we subscribed to.
   */
  public function onCancel(WorkflowTransitionEvent $event) {

    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
    $order = $event
      ->getEntity();
    $license_order_items = $this
      ->getLicensableOrderItems($order);
    foreach ($license_order_items as $order_item) {

      // Get the license from the order item.
      $license = $order_item
        ->get('license')->entity;
      if (!$license) {
        continue;
      }

      // Cancel the license.
      $transition = $license
        ->getState()
        ->getWorkflow()
        ->getTransition('cancel');
      $license
        ->getState()
        ->applyTransition($transition);
      $license
        ->save();
    }
  }

  /**
   * Returns the order items from an order which are for licensed products.
   *
   * @param \Drupal\commerce_order\Entity\OrderInterface $order
   *   The order entity.
   *
   * @return \Drupal\commerce_order\Entity\OrderItemInterface[]
   *   An array of the order items whose purchased products are for licenses.
   */
  protected function getLicensableOrderItems(OrderInterface $order) {
    $order_items = [];
    foreach ($order
      ->getItems() as $order_item) {

      // Skip order items that do not have a license reference field.
      // We check order items rather than the purchased entity to allow products
      // with licenses to be purchased without the checkout flow triggering
      // our synchronization. This is for cases such as recurring orders, where
      // the license entity should not be put through the normal workflow.
      // Checking the order item's bundle for our entity trait is expensive, as
      // it requires loading the bundle entity to call hasTrait() on it.
      // For now, just check whether the order item has our trait's field on it.
      // @see https://www.drupal.org/node/2894805
      if (!$order_item
        ->hasField('license')) {
        continue;
      }
      $purchased_entity = $order_item
        ->getPurchasedEntity();

      // This order item isn't "licensable" if the purchased entity it
      // references isn't properly configured.
      if (!$purchased_entity
        ->hasField('license_type') || $purchased_entity
        ->get('license_type')
        ->isEmpty()) {
        continue;
      }
      $order_items[] = $order_item;
    }
    return $order_items;
  }

  /**
   * Creates the license using the given order item.
   *
   * @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
   *   A licensable order item.
   *
   * @return \Drupal\commerce_license\Entity\LicenseInterface
   *   The created license.
   */
  protected function createLicenseFromOrderItem(OrderItemInterface $order_item) {

    /** @var \Drupal\commerce_license\LicenseStorageInterface $license_storage */
    $license_storage = $this->entityTypeManager
      ->getStorage('commerce_license');
    $license = $license_storage
      ->createFromOrderItem($order_item);

    // The license is "pending" until it gets activated, either when the order
    // gets paid, or if the license should be activated on order place.
    $license
      ->set('state', 'pending');
    $license
      ->save();

    // Set the license field on the order item so we have a reference
    // and can get hold of it in later events.
    $order_item->license = $license
      ->id();
    $order_item
      ->save();
    return $license;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
OrderSubscriber::$entityTypeManager protected property The entity type manager.
OrderSubscriber::createLicenseFromOrderItem protected function Creates the license using the given order item.
OrderSubscriber::getLicensableOrderItems protected function Returns the order items from an order which are for licensed products.
OrderSubscriber::getSubscribedEvents public static function Returns an array of event names this subscriber wants to listen to.
OrderSubscriber::onCancel public function Reacts to an order being cancelled.
OrderSubscriber::onPaid public function Activates the licenses on order paid.
OrderSubscriber::onPlace public function Reacts to an order being placed.
OrderSubscriber::__construct public function Constructs a new OrderSubscriber object.