diff --git a/commerce_license.services.yml b/commerce_license.services.yml index c1e6219..8eff590 100644 --- a/commerce_license.services.yml +++ b/commerce_license.services.yml @@ -7,4 +7,9 @@ services: plugin.manager.commerce_license_type: class: Drupal\commerce_license\LicenseTypeManager parent: default_plugin_manager - + commerce_license.license_order_sync_subscriber: + class: Drupal\commerce_license\EventSubscriber\LicenseOrderSyncSubscriber + arguments: + - '@entity_type.manager' + tags: + - { name: event_subscriber } diff --git a/src/Entity/License.php b/src/Entity/License.php index 0ec38a2..2fdcdbf 100644 --- a/src/Entity/License.php +++ b/src/Entity/License.php @@ -70,6 +70,7 @@ class License extends ContentEntityBase implements LicenseInterface { parent::preCreate($storage_controller, $values); $values += array( 'uid' => \Drupal::currentUser()->id(), + 'created' => \Drupal::service('datetime.time')->getRequestTime(), ); } diff --git a/src/EventSubscriber/LicenseOrderSyncSubscriber.php b/src/EventSubscriber/LicenseOrderSyncSubscriber.php new file mode 100644 index 0000000..4d81ce3 --- /dev/null +++ b/src/EventSubscriber/LicenseOrderSyncSubscriber.php @@ -0,0 +1,149 @@ +licenseStorage = $entity_type_manager->getStorage('commerce_license'); + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events = [ + // Events defined by state_machine, derived from the workflow defined in + // commerce_order.workflows.yml. + // See Drupal\state_machine\Plugin\Field\FieldType\StateItem::dispatchTransitionEvent() + + // Events for reaching the 'fulfillment' state. This depends on whether + // the workflow in use has validation or not. + 'commerce_order.place.post_transition' => ['onCartOrderFulfillment', -100], + 'commerce_order.validate.post_transition' => ['onCartOrderFulfillment', -100], + // Event for reaching the 'canceled' state. + 'commerce_order.cancel.post_transition' => ['onCartOrderCancel', -100], + ]; + return $events; + } + + /** + * Reacts to an order reaching fulfillment state. + * + * @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event + * The event we subscribed to. + */ + public function onCartOrderFulfillment(WorkflowTransitionEvent $event) { + // Only act if we are reaching the 'fulfillment' state. + if ($event->getToState()->getId() != 'fulfillment') { + return; + } + + /** @var \Drupal\commerce_order\Entity\OrderInterface $order */ + $order = $event->getEntity(); + + $license_order_items = $this->getOrderItemsWithLicensedProducts($order); + + foreach ($license_order_items as $order_item) { + // Create a new license. + $purchased_entity = $order_item->getPurchasedEntity(); + $license_type_plugin = $purchased_entity->get('license_type')->first()->getTargetInstance(); + + $license = $this->licenseStorage->create([ + 'type' => $license_type_plugin->getPluginId(), + 'state' => 'new', + 'product' => $purchased_entity->id(), + // Take the license owner from the order, for the case when orders are + // created for another user. + 'uid' => $order->uid, + // TODO: take the license's plugin-specific configuration from the + // product variation's license_type field plugin instance. + ]); + + $license->save(); + + // TODO: set the license field on the order item so we have a reference + // and can get hold of it in later events. + + // Attempt to activate the license. + $license->state = 'active'; + // TODO: how does a license type plugin indicate that it's not able to + // activate? And how do we notify the order at this point? + $license->save(); + } + } + + /** + * Reacts to an order being cancelled. + * + * @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event + * The event we subscribed to. + */ + public function onCartOrderCancel(WorkflowTransitionEvent $event) { + // TODO. + // Retrieve the license. For this we need https://www.drupal.org/node/2879276 + // Cancel the license. + } + + /** + * 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 getOrderItemsWithLicensedProducts(OrderInterface $order) { + $return_items = []; + + foreach ($order->getItems() as $order_item) { + $purchased_entity = $order_item->getPurchasedEntity(); + + // Skip purchased entities that do not grant a license. + // Checking the purchased entity's bundle for the trait is expensive, as + // it requires loading the bundle entity to call hasTrait() on it. + // For now, just check whether the purchased entity has our + // commerce_license trait's field on it. + // @see https://www.drupal.org/node/2894805 + if (!$purchased_entity->hasField('license_type')) { + continue; + } + + $return_items[] = $order_item; + } + + return $return_items; + } + +}