contacts_events-8.x-1.x-dev/src/Form/TicketInlineForm.php

src/Form/TicketInlineForm.php
<?php

namespace Drupal\contacts_events\Form;

use Drupal\Component\Utility\NestedArray;
use Drupal\contacts_events\Entity\TicketInterface;
use Drupal\contacts_events\Event\TicketPrepopulateEvent;
use Drupal\contacts_events\OrderStateTrait;
use Drupal\contacts_events\PriceCalculator;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\inline_entity_form\Element\InlineEntityForm;
use Drupal\inline_entity_form\Form\EntityInlineForm;
use Drupal\user\UserInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * The inline form for Ticket entities.
 */
class TicketInlineForm extends EntityInlineForm {

  use TicketFormHookTrait;
  use OrderStateTrait;

  /**
   * The price calculator service.
   *
   * @var \Drupal\contacts_events\PriceCalculator
   */
  protected $priceCalculator;

  /**
   * Configuration.
   *
   * @var \Drupal\Core\Config\ImmutableConfig
   */
  protected $bookingSettings;

  /**
   * Event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $dispatcher;

  /**
   * {@inheritdoc}
   */
  public function __construct(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, ModuleHandlerInterface $module_handler, EntityTypeInterface $entity_type, PriceCalculator $price_calculator, ImmutableConfig $booking_settings, EventDispatcherInterface $dispatcher) {
    parent::__construct($entity_field_manager, $entity_type_manager, $module_handler, $entity_type);
    $this->priceCalculator = $price_calculator;
    $this->bookingSettings = $booking_settings;
    $this->dispatcher = $dispatcher;
  }

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    return new static(
      $container->get('entity_field.manager'),
      $container->get('entity_type.manager'),
      $container->get('module_handler'),
      $entity_type,
      $container->get('contacts_events.price_calculator'),
      $container->get('config.factory')->get('contacts_events.booking_settings'),
      $container->get('event_dispatcher')
    );
  }

  /**
   * {@inheritdoc}
   *
   * @see hook_contacts_events_ticket_form_alter()
   */
  public function entityForm(array $entity_form, FormStateInterface $form_state) {
    // Get the entity we're building. The item in form state is going to be the
    // most up to date, if it's available.
    if ($ticket = $form_state->get('ticket')) {
      $entity_form['#entity'] = $ticket;
    }
    else {
      $ticket = $entity_form['#entity'];
    }
    /** @var \Drupal\contacts_events\Entity\TicketInterface $ticket */

    // Ensure we have the order item set on the ticket.
    if (!$ticket->getOrderItem()) {
      // Get the order item form element.
      $parents = array_slice($entity_form['#array_parents'], 0, -4);
      $order_item_element = &NestedArray::getValue($form_state->getCompleteForm(), $parents);
      $ticket->set('order_item', $order_item_element['#entity']);
    }

    // If this is the first ticket in the booking, and auto-fill first ticket is
    // enabled, then pre-populate the ticket info from the booking manager's
    // individual profile.
    if ($ticket->isNew() && $this->bookingSettings->get('autofill_first_ticket')) {
      $order = $ticket->getOrderItem()->getOrder();
      if (count($order->getItems()) == 0) {
        $this->copyUserDetailsToTicket($order->getCustomer(), $ticket);
      }
    }

    $entity_form = parent::entityForm($entity_form, $form_state);

    // Add checkbox for manually confirming the ticket.
    $this->addConfirmCheckbox($entity_form, $ticket, $form_state);

    // Add our update handler for the price.
    TicketForm::addPriceAjax(
      $entity_form,
      [[static::class, 'rebuildEntity']],
      $ticket->id() ?? 'new'
    );

    $this->alter($entity_form, $form_state, $ticket, $entity_form['#form_mode']);

    return $entity_form;
  }

  /**
   * Adds a checkbox allowing staff to manually confirm a ticket.
   *
   * @param array $form
   *   Form array.
   * @param \Drupal\contacts_events\Entity\TicketInterface $ticket
   *   Ticket being added/edited.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state.
   */
  protected function addConfirmCheckbox(array &$form, TicketInterface $ticket, FormStateInterface $form_state) {
    $form_mode = $form['#form_mode'];
    // Add in an option that allows staff to confirm tickets.
    if ($form_mode == 'booking_admin' && \Drupal::currentUser()->hasPermission('can manage bookings for contacts_events')) {
      // Only show the 'confirm ticket' option if the ticket is pending.
      if ($ticket->getStatus() == 'pending') {
        $form['confirm_wrapper'] = [
          '#type' => 'fieldset',
          '#weight' => 100,
          '#title' => new TranslatableMarkup('Confirm Ticket'),
        ];

        $booking_state = $ticket->getBooking()->getState()->value;
        if ($booking_state == 'confirmed' || $booking_state == 'modified') {
          $form['confirm_wrapper']['confirm_ticket'] = [
            '#type' => 'checkbox',
            '#title' => new TranslatableMarkup('By ticking this box, the ticket will be moved from the pending state to the Confirmed state'),
          ];

          // Add a new submit handler to handle confirming the ticket.
          // This is done as an ief_element_submit to ensure it runs
          // after the 'save' method has finished.
          $form['#ief_element_submit'][] = [$this, 'submitConfirmTicket'];
        }
        else {
          // If the booking is unconfirmed, show a message saying the booking
          // needs to be confirmed.
          $form['confirm_wrapper']['info'] = ['#markup' => new TranslatableMarkup('The booking has not been confirmed. Confirming this booking will also mark the ticket as confirmed. You should do this on the "View" tab.')];
        }
      }
    }
  }

  /**
   * Submission handler to rebuild the form with submitted values.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public static function rebuildEntity(array $form, FormStateInterface $form_state) {
    // Get the entity form.
    $entity_form_parents = $form_state->getTriggeringElement()['#array_parents'];
    array_pop($entity_form_parents);
    $entity_form = NestedArray::getValue($form, $entity_form_parents);

    /** @var \Drupal\contacts_events\Entity\TicketInterface $ticket */
    $ticket = $entity_form['#entity'];
    // Get the handler and trigger the rebuild.
    $inline_form_handler = InlineEntityForm::getInlineFormHandler($entity_form['#entity_type']);
    $inline_form_handler->buildEntity($entity_form, $ticket, $form_state);

    // Put the most up to date version into the form state and ensure the order
    // item is up to date.
    $form_state->set('ticket', $ticket);

    $form_state->setRebuild();
  }

  /**
   * {@inheritdoc}
   */
  public function buildEntity(array $entity_form, ContentEntityInterface $entity, FormStateInterface $form_state) {
    assert($entity instanceof TicketInterface);
    parent::buildEntity($entity_form, $entity, $form_state);

    // @todo Abstract ticket form acquisition into a trait for use with both
    // Ticket forms and Inline Entity forms.
    // Clear out contact for new tickets to allow for ajax updates.
    if ($entity->isNew()) {
      $entity->set('contact', NULL);
    }

    // Run an early acquisition, as ticket classes may respond to the contact.
    $entity->acquire(TRUE);

    // Recalculate the price of the ticket using the form handler.
    // @todo Something to optimize this and prevent it being calculated
    // mulitple time in a single request when nothing changes - perhaps an
    // onChange in the item and ticket?
    $this->priceCalculator->calculatePrice($entity->getOrderItem());
  }

  /**
   * {@inheritdoc}
   */
  public function save(EntityInterface $entity) {
    /** @var \Drupal\contacts_events\Entity\TicketInterface $entity */
    $order_item = $entity->getOrderItem();

    // Update the order item title.
    $order_item->set('title', $entity->getOrderItemTitle());

    // New ticket and order items reference each other and end in a loop. To
    // avoid that, clear the purchased entity off the order item, save it,
    // restore it and then save again.
    // OrderItemTicketInlineEntityWidget::submitSaveEntity ensures the correct
    // order item is tracked in the form.
    if ($entity->isNew() && $order_item && $order_item->isNew()) {
      // Also set the title, as doing it this way results in it not being
      // properly set.
      $order_item
        ->set('purchased_entity', NULL)
        ->save();

      $order_item
        ->set('purchased_entity', $entity)
        ->save();
    }
    // If it's not new, we can simply save.
    elseif ($order_item) {
      $order_item->save();
    }

    // Ensure the right order item entity is on the ticket.
    $entity->setOrderItem($order_item);

    // Now we can proceed to save the ticket.
    $entity->save();
  }

  /**
   * Callback for handling confirming the ticket.
   *
   * If the user selected the 'confirm ticket' checkbox, then confirm it.
   * Note that this runs after the save method, so the ticket will already
   * have been saved once by the time this is called.
   *
   * @param array $entity_form
   *   The entity form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state.
   */
  public function submitConfirmTicket(array $entity_form, FormStateInterface $form_state) {
    $ticket_data = $form_state->getValue($entity_form['#parents']);

    if ($ticket_data['confirm_wrapper']['confirm_ticket'] ?? FALSE) {
      /** @var \Drupal\contacts_events\Entity\TicketInterface $ticket */
      $ticket = $entity_form['#entity'];
      /** @var \Drupal\state_machine\Plugin\Field\FieldType\StateItem $state */
      $state = $ticket->getOrderItem()->get('state')->first();
      $this->applyTransitionIfAllowed($state, 'confirm', TRUE);
    }
  }

  /**
   * Copies information from the individual profile to the ticket.
   *
   * @param \Drupal\user\UserInterface $user
   *   The user to copy.
   * @param \Drupal\contacts_events\Entity\TicketInterface $ticket
   *   The target ticket.
   */
  protected function copyUserDetailsToTicket(UserInterface $user, TicketInterface $ticket) {
    if (isset($user->profile_crm_indiv->entity)) {
      $profile = $user->get('profile_crm_indiv')->entity;
      $ticket->set('contact', $user);
      $ticket->set('name', $profile->get('crm_name')->getValue());
      $ticket->set('email', $profile->getOwner()->getEmail());
    }
    // Allow other modules to perform their own prepopulate.
    $this->dispatcher->dispatch(TicketPrepopulateEvent::NAME, new TicketPrepopulateEvent($ticket, $user));
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc