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

src/Form/TicketForm.php
<?php

namespace Drupal\contacts_events\Form;

use Drupal\Component\Utility\NestedArray;
use Drupal\contacts_events\Element\AjaxUpdate;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;

/**
 * Form controller for Ticket edit forms.
 *
 * @ingroup contacts_events
 */
class TicketForm extends ContentEntityForm {

  use TicketFormHookTrait;

  /**
   * The ticket entity.
   *
   * @var \Drupal\contacts_events\Entity\TicketInterface
   */
  protected $entity;

  /**
   * {@inheritdoc}
   *
   * @see hook_contacts_events_ticket_form_alter()
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\contacts_events\Entity\Ticket $entity */
    $form = parent::buildForm($form, $form_state);

    // Add our update handler for the price.
    static::addPriceAjax($form, ['::rebuildForm']);

    $this->alter($form, $form_state, $this->entity, $this->getOperation());

    return $form;
  }

  /**
   * Add the price update ajax handler.
   *
   * @param array $form
   *   The entity form.
   * @param array $submit_handlers
   *   An array of submit handlers.
   * @param string|null $unique_suffix
   *   A unique suffix for the update name and wrapper ID.
   */
  public static function addPriceAjax(array &$form, array $submit_handlers, $unique_suffix = NULL) {
    // If the price map isn't on the form, we wont do anything.
    if (!isset($form['mapped_price'])) {
      return;
    }

    // Add our update handler for the price.
    $price_update = AjaxUpdate::createElement();
    $form['price_update'] = &$price_update->getRenderArray('price_update', $unique_suffix);
    $form['price_update']['#submit'] = $submit_handlers;

    // Ensure messages are shown at the top of the relevant part of the form.
    $form['messages'] = [
      '#type' => 'container',
      '#id' => $price_update->getIdWithUniqueSuffix('ticket-messages'),
      '#weight' => -99,
    ];
    // Only actually output the messages for AJAX requests.
    if (\Drupal::request()->isXmlHttpRequest()) {
      $form['messages']['messages'] = ['#type' => 'status_messages'];
    }
    $price_update->registerElementToUpdate($form['messages']);

    // Register the mapped price to be updated.
    $form['mapped_price']['#id'] = 'ticket-price-ajax-wrapper' . ($unique_suffix ? '-' . $unique_suffix : '');
    $price_update->registerElementToUpdate($form['mapped_price']);
    $price_update->registerElementToRespondTo($form['mapped_price']['widget'][0]['class'], [], $form['mapped_price']['widget']['#parents']);

    if (isset($form['mapped_price']['widget'][0]['class_overridden'])) {
      $price_update->registerElementToRespondTo($form['mapped_price']['widget'][0]['class_overridden'], [], FALSE);
    }

    if (isset($form['mapped_price']['widget'][0]['class_full'])) {
      $price_update->registerElementToRespondTo($form['mapped_price']['widget'][0]['class_full'], [], FALSE);
    }

    if (isset($form['mapped_price']['widget'][0]['booking_window_overridden'])) {
      $price_update->registerElementToRespondTo($form['mapped_price']['widget'][0]['booking_window_overridden'], [], FALSE);
    }

    if (isset($form['mapped_price']['widget'][0]['booking_window_full'])) {
      $price_update->registerElementToRespondTo($form['mapped_price']['widget'][0]['booking_window_full'], [], FALSE);
    }

    // Register the date of birth to trigger an update.
    if (isset($form['date_of_birth'])) {
      $element = &$form['date_of_birth']['widget'][0]['value'];

      switch ($element['#type']) {
        // Datetime uses a single element. Assume the default is the same.
        case 'datetime':
        default:
          $options = [
            'event' => 'delayed.change',
            'no_disable' => TRUE,
            'disable-refocus' => TRUE,
          ];
          $price_update->registerElementToRespondTo($form['date_of_birth']['widget'][0]['value'], $options, $form['date_of_birth']['widget']['#parents']);
          $element['#attached']['library'][] = 'contacts_events/delayed_events';

          // Mark the date value element as #ajax_processed as #ajax is copied
          // to the children, resulting in a double submission.
          $element['#ajax_processed'] = TRUE;
          break;

        // The datelist element gets exanded, so we need to add our AJAX after
        // the expansion.
        case 'datelist':
          /** @var \Drupal\Core\Render\ElementInfoManagerInterface $info */
          $info = \Drupal::service('plugin.manager.element_info');
          $element['#process'] = $info->getInfoProperty($element['#type'], '#process');
          $element['#process'][] = [static::class, 'processDateComponentAjax'];
          $price_update->registerLimitValidationErrors($form['date_of_birth']['widget']['#parents']);
          break;
      }
    }

    // Register the price override to trigger an update.
    if (isset($form['price_override'])) {
      $options = [
        'event' => 'delayed.keyup',
        'no_disable' => TRUE,
        'disable-refocus' => TRUE,
      ];
      $price_update->registerElementToRespondTo($form['price_override']['widget'][0], $options, $form['price_override']['widget']['#parents']);

      if (isset($form['date_of_birth'])) {
        $form['date_of_birth']['widget'][0]['value']['#attached']['library'][] = 'contacts_events/delayed_events';
      }
    }

    // Register the email address to trigger an update.
    if (isset($form['email'])) {
      $options = [
        'event' => 'change',
        'no_disable' => TRUE,
        'disable-refocus' => TRUE,
      ];
      $price_update->registerElementToRespondTo($form['email']['widget'][0]['value'], $options, $form['email']['widget']['#parents']);
    }
  }

  /**
   * Add the AJAX callbacks to components of a date field.
   *
   * @param array $element
   *   The form element whose value is being processed.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param array $complete_form
   *   The complete form structure.
   */
  public static function processDateComponentAjax(array &$element, FormStateInterface $form_state, array &$complete_form) {
    $options = [
      'event' => 'delayed.change',
      'no_disable' => TRUE,
      'disable-refocus' => TRUE,
      // Group all the date components in a single delayed AJAX event and
      // increase the timeout.
      'delayed_group_id' => $element['#id'],
      'delayed_time' => 1000,
    ];
    $element['#attached']['library'][] = 'contacts_events/delayed_events';

    // Get the ajax update element for the price for the ticket form.
    $ticket_form = &static::getTicketForm($complete_form, $element);

    /** @var \Drupal\contacts_events\Element\AjaxUpdate $price_update */
    if (isset($ticket_form['price_update']['#element']) && $price_update = $ticket_form['price_update']['#element']) {
      foreach (Element::children($element) as $component) {
        $price_update->registerElementToRespondTo($element[$component], $options, FALSE);
      }
    }

    return $element;
  }

  /**
   * Form submission handler to rebuild the form.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function rebuildForm(array &$form, FormStateInterface $form_state) {
    $this->submitForm($form, $form_state);
    $form_state->setRebuild();
  }

  /**
   * {@inheritdoc}
   */
  public function buildEntity(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\contacts_events\Entity\TicketInterface $entity */
    $entity = parent::buildEntity($form, $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);

    // Get the order item, ensuring our working ticket is set on it.
    $order_item = $entity->getOrderItem();
    $order_item->set('purchased_entity', $entity);

    // Recalculate the price and mapping.
    \Drupal::service('contacts_events.price_calculator')->calculatePrice($order_item);

    return $entity;
  }

  /**
   * {@inheritdoc}
   *
   * @todo Handle the create scenario or prevent it entirely.
   */
  public function save(array $form, FormStateInterface $form_state) {
    $entity = $this->entity;
    $status = parent::save($form, $form_state);

    // Save the order item.
    $entity->getOrderItem()->save();

    switch ($status) {
      case SAVED_NEW:
        $this->messenger()->addMessage($this->t('Created the %label Ticket.', [
          '%label' => $entity->label(),
        ]));
        break;

      default:
        $this->messenger()->addMessage($this->t('Saved the %label Ticket.', [
          '%label' => $entity->label(),
        ]));
    }
    $form_state->setRedirect('entity.contacts_ticket.canonical', ['contacts_ticket' => $entity->id()]);
  }

  /**
   * Get the closest ticket form to an element.
   *
   * @param array $complete_form
   *   The complete form.
   * @param array $element
   *   The element we want the closest ticket form for.
   *
   * @return array
   *   The ticket form.
   */
  public static function &getTicketForm(array &$complete_form, array $element) {
    $array_parents = $element['#array_parents'];

    do {
      array_pop($array_parents);
      $form = &NestedArray::getValue($complete_form, $array_parents);
      $entity_type = $form['#entity_type'] ?? FALSE;
    } while ($entity_type != 'contacts_ticket' && count($array_parents) > 1);

    return $form;
  }

}

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

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