contacts_events-8.x-1.x-dev/modules/accommodation/src/Plugin/Field/FieldWidget/BookingCampingWidget.php

modules/accommodation/src/Plugin/Field/FieldWidget/BookingCampingWidget.php
<?php

namespace Drupal\contacts_events_accommodation\Plugin\Field\FieldWidget;

use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase;
use Drupal\Component\Utility\NestedArray;
use Drupal\contacts_events_accommodation\AccommodationHelper;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Widget for camping on bookings.
 *
 * @FieldWidget(
 *   id = "order_items_camping",
 *   label = @Translation("Booking Camping"),
 *   field_types = {
 *     "entity_reference"
 *   },
 *   multiple_values = true
 * )
 */
class BookingCampingWidget extends WidgetBase implements ContainerFactoryPluginInterface {

  /**
   * The context for string translation.
   */
  const TRANSLATION_CONTEXT = 'contacts_events_booking_camping_widget_accommodation_error';

  /**
   * The accommodation helper.
   *
   * @var \Drupal\contacts_events_accommodation\AccommodationHelper
   */
  protected $accommodationHelper;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $currentUser;

  /**
   * Constructs the Camping widget object.
   *
   * @param string $plugin_id
   *   The plugin_id for the widget.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
   *   The definition of the field to which the widget is associated.
   * @param array $settings
   *   The widget settings.
   * @param array $third_party_settings
   *   Any third party settings.
   * @param \Drupal\contacts_events_accommodation\AccommodationHelper $accommodation_helper
   *   The accommodation helper.
   * @param \Drupal\Core\Session\AccountInterface $current_user
   *   The current user.
   */
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, AccommodationHelper $accommodation_helper, AccountInterface $current_user) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    $this->accommodationHelper = $accommodation_helper;
    $this->currentUser = $current_user;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $widget = new static(
      $plugin_id,
      $plugin_definition,
      $configuration['field_definition'],
      $configuration['settings'],
      $configuration['third_party_settings'],
      $container->get('contacts_events_accommodation.helper'),
      $container->get('current_user')
    );
    $widget->setMessenger($container->get('messenger'));
    return $widget;
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $manage = $this->currentUser->hasPermission('can manage bookings for contacts_events');
    $booking_accommodation = $this->accommodationHelper->getBookingHelper($items);

    $order = $items->getEntity();
    $event = $order->get('event')->entity;
    foreach ($this->accommodationHelper->getAccommodation($event, 'camping') as $id => $accommodation) {
      $max = $booking_accommodation->getMaxAllowedAccommodation($accommodation);

      // If we have manage permission, always allow setting more than the
      // current amount.
      if ($manage) {
        $max += 10;
      }

      // If we are not allowed any, don't display.
      if (!$max) {
        continue;
      }

      $element[$id] = [
        '#type' => 'select',
        '#title' => $accommodation->label(),
        '#description' => $accommodation->getDescription(),
        '#options' => range(0, $max),
        '#default_value' => $booking_accommodation->getTotalAccommodation($id),
      ];
      if ($confirmed = $booking_accommodation->getConfirmedAccommodation($id)) {
        $element[$id]['#options'][$confirmed] = new TranslatableMarkup('@count (confirmed)', [
          '@count' => $confirmed,
        ]);
      }
    }

    // Use a process to add the submit handler.
    $element['#process'][] = [$this, 'processElement'];

    // Add element level validation and the data required for it.
    $element['#element_validate'][] = [$this, 'validateTotal'];
    $element['#total_delegates'] = $booking_accommodation->getTotalDelegates();

    // Set the title/description and make it a fieldset.
    $element['#type'] = 'fieldset';
    $element['#title'] = $this->t('Camping');

    // @todo Switch to #description and #description_display => 'before' when
    // https://www.drupal.org/project/drupal/issues/2396145 is fixed.
    $element['_description'] = [
      '#type' => 'html_tag',
      '#tag' => 'div',
      '#attributes' => ['class' => ['description']],
      '#value' => $this->t('Please select the equipment you would like to bring with you.'),
      '#weight' => -99,
    ];
    $element['#required'] = FALSE;

    return $element;
  }

  /**
   * Process callback to add the submit handler.
   *
   * @param array $element
   *   The widget element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $complete_form
   *   The complete form.
   *
   * @return array
   *   The widget element.
   */
  public function processElement(array $element, FormStateInterface $form_state, array &$complete_form) {
    // If we are inside an inline entity form, we can hook into that.
    if ($form_state->getFormObject() instanceof CheckoutFlowWithPanesBase) {
      // Run up our form until we hit our pane and add our submit handler there
      // for the pane to pick up.
      $path = $element['#array_parents'];
      do {
        $pane_element = &NestedArray::getValue($complete_form, $path);
        array_pop($path);
      } while (@$pane_element['#theme'] != 'commerce_checkout_pane' && !empty($path));

      // Check we have found the pane element.
      if ($pane_element['#theme'] == 'commerce_checkout_pane') {
        $pane_element['#pane_submit'] = [];
        $handlers = &$pane_element['#pane_submit'];
      }
    }
    elseif (isset($complete_form['actions']['submit']['#submit'])) {
      $handlers = &$complete_form['actions']['submit']['#submit'];
    }
    elseif (isset($complete_form['#submit'])) {
      $handlers = &$complete_form['#submit'];
    }

    // If we have handlers, insert our save handler before the save.
    if (isset($handlers)) {
      // Get our array of locations.
      $locations = $form_state->get('contacts_events_accommodation_camping_parents') ?? [];

      // Only add the submit handler once.
      if (empty($locations)) {
        $position = array_search($search ?? '::save', $handlers) ?: 0;
        array_splice($handlers, $position, 0, [[$this, 'save']]);
      }

      // Store the location in form state so we can ensure we're working with
      // the correct items.
      $locations[] = array_slice($element['#parents'], 0, -1);
      $form_state->set('contacts_events_accommodation_camping_parents', $locations);
    }

    return $element;
  }

  /**
   * Element validation to check the total accommodation is not too low or high.
   *
   * @param array $element
   *   The camping element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $form
   *   The complete form.
   */
  public function validateTotal(array $element, FormStateInterface $form_state, array $form) {
    $result = $this->accommodationHelper->validateAccommodationTotal($element['#total_delegates'], $form_state->getValue($element['#parents'], []));

    $delegates = $this->formatPlural(
      $element['#total_delegates'],
      'delegate',
      '@count delegates',
      [],
      ['context' => self::TRANSLATION_CONTEXT],
    );

    switch ($result) {
      case AccommodationHelper::VALIDATE_OK:
        // No error, so nothing to do.
        break;

      case AccommodationHelper::VALIDATE_TOO_LITTLE:
        $error = $this->t(
          'It looks like you have not selected enough accommodation for the @count you are booking for.',
          ['@count' => $delegates],
          ['context' => self::TRANSLATION_CONTEXT],
        );
        break;

      case AccommodationHelper::VALIDATE_TOO_MUCH:
        $error = $this->t(
          'It looks like you have selected too much accommodation for the @count you are booking for.',
          ['@count' => $delegates],
          ['context' => self::TRANSLATION_CONTEXT],
        );
        break;

      default:
        $error = $this->t(
          'Sorry, we encountered an unknown error.',
          [],
          ['context' => self::TRANSLATION_CONTEXT],
        );
        break;
    }

    if (isset($error)) {
      if ($this->currentUser->hasPermission('can manage bookings for contacts_events')) {
        $this->messenger()->addWarning($error);
      }
      else {
        $form_state->setError($element, $error);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
    // Get the field state.
    $items_to_save = $this->getItemsToSave($form['#parents'], $form_state);

    // Create or modify the relevant order items.
    $booking_accommodation = $this->accommodationHelper->getBookingHelper($items);
    $path = array_merge($form['#parents'], [$this->fieldDefinition->getName()]);
    $items_to_save = $booking_accommodation->updateUnconfirmedItems($form_state->getValue($path, [])) + $items_to_save;

    // Store for later and return the values.
    $this->setItemsToSave($form['#parents'], $form_state, $items_to_save);
    return $items->getValue();
  }

  /**
   * Submit callback to save the order items prior to the order being saved.
   *
   * @param array $form
   *   The form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   */
  public function save(array $form, FormStateInterface $form_state) {
    $locations = $form_state->get('contacts_events_accommodation_camping_parents');

    foreach ($locations as $parents) {
      $items_to_save = $this->getItemsToSave($parents, $form_state);

      // Save any items that need saving/deleting.
      /** @var \Drupal\commerce_order\Entity\OrderItemInterface $item */
      foreach ($items_to_save as $item) {

        if ((int) $item->getQuantity() === 0 && !$item->isNew()) {
          $item->delete();
        }
        else {
          $item->save();
        }
      }

      $this->setItemsToSave($parents, $form_state, []);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function isApplicable(FieldDefinitionInterface $field_definition) {
    // Only allow for order items on the contacts booking bundle of orders.
    return $field_definition->getTargetEntityTypeId() == 'commerce_order'
      && $field_definition->getName() == 'order_items';
  }

  /**
   * Get the items to save.
   *
   * We don't use the field state as inline entity form clears that too early.
   *
   * @param array $parents
   *   The form parents of the field widget.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return array
   *   The items to save.
   */
  public function getItemsToSave(array $parents, FormStateInterface $form_state) : array {
    $path = $parents;
    array_unshift($path, static::class);
    $path[] = $this->fieldDefinition->getName();
    return $form_state->get($path) ?? [];
  }

  /**
   * Set the items to save.
   *
   * We don't use the field state as inline entity form clears that too early.
   *
   * @param array $parents
   *   The form parents of the field widget.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $items
   *   The items to save.
   */
  public function setItemsToSave(array $parents, FormStateInterface $form_state, array $items) : void {
    $path = $parents;
    array_unshift($path, static::class);
    $path[] = $this->fieldDefinition->getName();
    $form_state->set($path, $items);
  }

}

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

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