contacts_events-8.x-1.x-dev/modules/teams/src/EventSubscriber/TeamTicketStateSubscriber.php
modules/teams/src/EventSubscriber/TeamTicketStateSubscriber.php
<?php
namespace Drupal\contacts_events_teams\EventSubscriber;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\contacts_events\OrderEventTransitionTrait;
use Drupal\contacts_events\OrderStateTrait;
use Drupal\contacts_events\PriceCalculator;
use Drupal\contacts_events_teams\TeamQueries;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Event subscriber for handling team ticket state transitions.
*
* @package Drupal\contacts_events_teams\EventSubscriber
*/
class TeamTicketStateSubscriber implements EventSubscriberInterface {
use OrderStateTrait;
use OrderEventTransitionTrait;
/**
* The price calculator.
*
* @var \Drupal\contacts_events\PriceCalculator
*/
protected $calculator;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The team queries service.
*
* @var \Drupal\contacts_events_teams\TeamQueries
*/
protected $queries;
/**
* Construct the team ticket state subscriber event subscriber.
*
* @param \Drupal\contacts_events\PriceCalculator $calculator
* The price calculator.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher service.
* @param \Drupal\contacts_events_teams\TeamQueries $queries
* The team queries service.
*/
public function __construct(PriceCalculator $calculator, EventDispatcherInterface $event_dispatcher, TeamQueries $queries) {
$this->calculator = $calculator;
$this->eventDispatcher = $event_dispatcher;
$this->queries = $queries;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// phpcs:disable Drupal.Arrays.Array.LongLineDeclaration
// After a team ticket has been confirmed, move to application in progress.
$events['commerce_order.place.pre_transition'][] = ['beginTeamApplicationProcess', 500];
// When a team application moves to a different state update the ticket
// state to match.
$events['contacts_events_teams_applications.post_transition'][] = ['updateTicketState'];
// When a ticket goes back to pending, clear the team details.
$events['contacts_events_order_items.team_app_back_to_pending.pre_transition'][] = ['resetTeamTicket'];
$events['contacts_events_order_items.cancel.pre_transition'][] = ['resetTeamTicket'];
// Make sure the confirm events get fired for team tickets.
$events['contacts_events_order_items.team_app_in_progress.pre_transition'][] = ['fireOrderItemConfirmedEvents', -999];
$events['contacts_events_order_items.team_app_in_progress.post_transition'][] = ['fireOrderItemConfirmedEvents', 90];
// phpcs:enable
return $events;
}
/**
* Check whether this subscriber applies to the event.
*
* @param \Drupal\commerce_order\Entity\OrderInterface $order
* The order we are checking.
*
* @return bool
* Whether this subscriber applies to the given order.
*/
protected function applies(OrderInterface $order) {
return $order->bundle() == 'contacts_booking';
}
/**
* Triggered after the order item has moved to the confirmed state.
*
* If this is a team ticket, then start the application process.
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* The workflow transition event.
*
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
*/
public function beginTeamApplicationProcess(WorkflowTransitionEvent $event) {
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $event->getEntity();
if (!$this->applies($order)) {
return;
}
foreach ($order->getItems() as $order_item) {
if ($order_item->bundle() === 'contacts_ticket') {
/** @var \Drupal\state_machine\Plugin\Field\FieldType\StateItem $state */
$state = $order_item->get('state')->first();
$this->applyTransitionIfAllowed($state, 'team_app_in_progress', TRUE);
}
}
}
/**
* Updates the ticket state to match the team application state.
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* Transition event on the team app.
*
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function updateTicketState(WorkflowTransitionEvent $event) {
$transition = $event->getTransition();
$to_state = $transition->getToState()->getId();
// If we are transitioning to rejected_notified or archive, the ticket
// should go back to pending.
if (in_array($to_state, ['rejected_notified', 'archived'])) {
$order_item_transition = 'team_app_back_to_pending';
// The team_app_back_to_pending transition causes the team details
// to be removed from the ticket. If we're in the middle of transferring
// a team application from one team to another then we explicitly *don't*
// want this to happen. The transfer process will have flagged that this
// is happening using a surrogate property.
if ($event->getEntity()->is_transferring) {
return;
}
}
// If it is to approved, we should allow the ticket to be moved to paid in
// full.
elseif ($to_state == 'approved') {
$order_item_transition = 'paid_in_full';
}
// Otherwise, do nothing.
else {
return;
}
/** @var \Drupal\contacts_events_teams\Entity\TeamApplication */
$app = $event->getEntity();
/** @var \Drupal\contacts_events\Entity\Ticket $ticket */
$ticket = $app->get('ticket')->entity;
$order_item = $ticket->getOrderItem();
$this->applyTransitionIfAllowed($order_item->get('state')->first(), $order_item_transition, TRUE);
}
/**
* Triggered when a team ticket is reset back to pending.
*
* If this is a team ticket, then start the application process.
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* The workflow transition event.
*
* @throws \Drupal\Core\TypedData\Exception\MissingDataException
*/
public function resetTeamTicket(WorkflowTransitionEvent $event) {
/** @var \Drupal\commerce_order\Entity\OrderItem $order_item */
$order_item = $event->getEntity();
if ($order_item->bundle() === 'contacts_ticket') {
/** @var \Drupal\contacts_events\Entity\TicketInterface $ticket */
$ticket = $order_item->getPurchasedEntity();
// If there is a team application for the ticket being cancelled then
// archive it.
if ($application = $this->queries->getTeamApplicationForTicket($ticket)) {
/** @var \Drupal\state_machine\Plugin\Field\FieldType\StateItem $application_state */
$application_state = $application->get('state')->first();
$this->applyTransitionIfAllowed($application_state, 'archive', TRUE);
}
$ticket->set('team', NULL);
$ticket->set('is_team_ticket', NULL);
$this->calculator->calculatePrice($order_item);
$ticket->save();
}
}
/**
* Manually fire the confirmed events for team ticket confirmations.
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* The confirmed_paid_in_full transition event.
* @param string $event_name
* The event name.
*/
public function fireOrderItemConfirmedEvents(WorkflowTransitionEvent $event, $event_name) {
/** @var \Drupal\commerce_order\Entity\OrderItemInterface $entity */
$entity = $event->getEntity();
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $entity->getOrder();
if (!$this->applies($order)) {
return;
}
// We have bypassed the normal confirm transition so trigger any events that
// we may have missed.
$event_parts = explode('.', $event_name);
$phase = $event_parts[2];
$this->dispatchTransitionEvent('confirm', $phase, $event, $entity);
}
}
