contacts_events-8.x-1.x-dev/modules/villages/src/Plugin/Field/FieldWidget/CampingVillageGroupWidget.php

modules/villages/src/Plugin/Field/FieldWidget/CampingVillageGroupWidget.php
<?php

namespace Drupal\contacts_events_villages\Plugin\Field\FieldWidget;

use Drupal\Component\Utility\NestedArray;
use Drupal\contacts_events\Entity\EventInterface;
use Drupal\contacts_events_villages\Plugin\VillageGroupTypeManager;
use Drupal\Core\Entity\EntityTypeManagerInterface;
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\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Widget for village group on bookings.
 *
 * @FieldWidget(
 *   id = "village_group_camping",
 *   label = @Translation("Camping Village Group"),
 *   field_types = {
 *     "entity_reference"
 *   }
 * )
 */
class CampingVillageGroupWidget extends WidgetBase implements ContainerFactoryPluginInterface {

  /**
   * The village group storage service.
   *
   * @var \Drupal\Core\Entity\ContentEntityStorageInterface
   */
  protected $villageGroupStorage;

  /**
   * The village group type plugin manager.
   *
   * @var \Drupal\contacts_events_villages\Plugin\VillageGroupTypeManager
   */
  protected $villageTypeManager;

  /**
   * 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\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\contacts_events_villages\Plugin\VillageGroupTypeManager $village_type_manager
   *   The village group type plugin manager.
   */
  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, EntityTypeManagerInterface $entity_type_manager, VillageGroupTypeManager $village_type_manager) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    $this->villageGroupStorage = $entity_type_manager->getStorage('c_events_village_group');
    $this->villageTypeManager = $village_type_manager;
  }

  /**
   * {@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('entity_type.manager'),
      $container->get('plugin.manager.c_events_village_group_type')
    );
    $widget->setMessenger($container->get('messenger'));
    return $widget;
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $items */
    $booking = $items->getEntity();
    $event = $booking->get('event')->entity;

    if ($event->get('village_group_types')->isEmpty()) {
      return FALSE;
    }

    // Store the event ID for ::massageFormValues.
    $element['#event_id'] = $event->id();

    $element['target_id'] = [
      '#type' => 'radios',
      '#title' => $element['#title'],
      '#title_display' => 'invisible',
      '#default_value' => $items->getValue()[0]['target_id'] ?? NULL,
      '#required' => $element['#required'] ?? FALSE,
    ];

    $options = [];
    $subform = [];
    $unexpanded_types = [];

    $types = array_map(function ($item) {
      return $item['value'];
    }, $event->get('village_group_types')->getValue());

    foreach ($types as $type) {
      $definition = $this->villageTypeManager->getDefinition($type);

      // Make sure the plugin exists.
      if (!$definition) {
        continue;
      }

      /** @var \Drupal\contacts_events_villages\Plugin\VillageGroupTypeInterface $instance */
      $instance = $this->villageTypeManager->createInstance($type);
      if ($instance->isExpanded()) {
        foreach ($this->getEventGroups($event, $type) as $group) {
          $options[$group->id()] = $group->label();
          $element['target_id'][$group->id()]['#description'] = $group->getDescription();
        }
      }
      else {
        $unexpanded_types[$type] = $type;
        $options[$type] = $definition['label'];
        $element['target_id'][$type]['#description'] = $definition['description'] ?? '';
        $subform[$type] = $instance->form($items);
      }
    }

    $element['target_id']['#options'] = $options;
    $element['#theme_wrappers'] = ['form_element'];
    $element['#description_display'] = 'before';

    // Check default target type.
    if ($element['target_id']['#default_value']) {
      $target_entity = $this->villageGroupStorage->load($element['target_id']['#default_value']);
      if ($target_entity && in_array($target_entity->bundle(), $unexpanded_types)) {
        $element['target_id']['#default_value'] = $target_entity->bundle();
      }
    }

    if (!empty($subform)) {
      $element['subform'] = $subform;
      $element['subform']['#weight'] = 99;
      $element['#process'][] = [$this, 'processElement'];
    }

    return $element;
  }

  /**
   * Process callback to add widget option states to subforms.
   *
   * @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) {
    $nested_path = $this->getElementPathNotated($element);
    foreach (Element::children($element['subform']) as $plugin) {
      // Make the subform only visible when the appropriate option is selected.
      if (!isset($element['subform'][$plugin]['#type'])) {
        $element['subform'][$plugin]['#type'] = 'container';
      }

      $element['subform'][$plugin]['#states'] = [
        'visible' => [
          ':input[name="' . $nested_path . '[target_id]"]' => ['value' => $plugin],
        ],
      ];

      // Allow subform to do additional processing.
      /** @var \Drupal\contacts_events_villages\Plugin\VillageGroupTypeInterface $plugin_instance */
      $plugin_instance = $this->villageTypeManager->createInstance($plugin);
      $plugin_instance->process($element['subform'][$plugin], $form_state, $complete_form, $nested_path);

      // Setup validation of the subform.
      // phpcs:ignore Drupal.Arrays.Array.LongLineDeclaration
      $element['subform'][$plugin]['#element_validate'][] = [$this, 'validateSubForm'];
    }

    // Workaround for massageFormValues() not getting $element.
    $element['array_parents'] = [
      '#type' => 'value',
      '#value' => $element['#array_parents'],
    ];

    return $element;
  }

  /**
   * Performs validation of village group subforms.
   *
   * @param array $subform
   *   The sub-form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Current form state.
   * @param array $complete_form
   *   The complete form.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginException
   */
  public function validateSubForm(array &$subform, FormStateInterface $form_state, array &$complete_form) {
    $plugin_id = end($subform['#parents']);
    // Traverse up the parent hierarchy to find the selected option.
    // Remove the last 2 elements from parents (the plugin id, and the subform
    // container) to get the path to the radio list.
    $relevant_parents = $subform['#parents'];
    $relevant_parents = array_splice($relevant_parents, 0, -2);
    $selected_option = $form_state->getValue($relevant_parents)['target_id'];

    // If the selected option is the one that controls the current subform
    // then allow the subform to validate.
    if ($selected_option == $plugin_id) {
      /** @var \Drupal\contacts_events_villages\Plugin\VillageGroupTypeInterface $plugin */
      $plugin = $this->villageTypeManager->createInstance($plugin_id);
      // Invoke validate only if this form is the currently selected plugin.
      $plugin->validate($subform, $form_state, $complete_form);
    }
  }

  /**
   * Get the notated element path.
   *
   * @param array $element
   *   Form element.
   *
   * @return string
   *   Concatenated and notated path like "wrapper[field][0]".
   *
   * @todo Surely there is a Utility method in core for this?
   */
  protected function getElementPathNotated(array $element) {
    $parents = $element['#parents'];
    $widget_path = array_shift($parents);
    foreach ($parents as $parent) {
      $widget_path .= "[$parent]";
    }

    return $widget_path;
  }

  /**
   * Get available group entities for the given event.
   *
   * @param \Drupal\contacts_events\Entity\EventInterface $event
   *   The event to check for.
   * @param string|null $type
   *   (Optional) The group type to filter by.
   *
   * @return \Drupal\contacts_events_villages\Entity\VillageGroupInterface[]
   *   Array of relevant group entities keyed by entity id.
   */
  protected function getEventGroups(EventInterface $event, $type = NULL) {
    $query = $this->villageGroupStorage->getQuery()
      ->condition('event', $event->id())
      ->sort('name', 'ASC');
    $query->accessCheck(TRUE);

    if ($type) {
      $query->condition('type', $type);
    }

    return $this->villageGroupStorage->loadMultiple($query->execute());
  }

  /**
   * {@inheritdoc}
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    foreach ($values as &$item) {
      $element = NestedArray::getValue($form_state->getCompleteForm(), $item['array_parents']);

      if (!empty($item['target_id']) && !is_numeric($item['target_id'])) {
        $village_type = $item['target_id'];
        /** @var \Drupal\contacts_events_villages\Plugin\VillageGroupTypeInterface $type_plugin */
        $type_plugin = $this->villageTypeManager->createInstance($village_type);

        // Build a dummy entity to compare to.
        /** @var \Drupal\contacts_events_villages\Entity\VillageGroup $dummy_group */
        $dummy_group = $this->villageGroupStorage->create(['type' => $village_type]);
        $dummy_group->setEvent($element['#event_id']);
        $dummy_group = $type_plugin->buildFormEntity($item['subform'][$village_type], $dummy_group);

        $existing = $type_plugin->getExisting($dummy_group);

        if ($existing->isNew()) {
          $existing->save();
        }

        $item['target_id'] = $existing->id();
      }
    }
    return $values;
  }

  /**
   * {@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->getTargetBundle() == 'contacts_booking'
      && $field_definition->getName() == 'village_group';
  }

}

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

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