eav_field-2.x-dev/src/Plugin/Field/FieldWidget/EavWidget.php

src/Plugin/Field/FieldWidget/EavWidget.php
<?php

namespace Drupal\eav_field\Plugin\Field\FieldWidget;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\Attribute\FieldWidget;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Field\WidgetPluginManager;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\eav_field\EavAttributeStorage;
use Drupal\eav_field\Entity\EavAttribute;
use Drupal\eav_field\Entity\EavAttributeInterface;
use Drupal\eav_field\Entity\EavValue;
use Drupal\eav_field\Plugin\Field\FieldType\EavItemInterface;
use Drupal\eav_field\Plugin\Field\FieldType\EavItemListInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

#[FieldWidget(
  id: 'eav_widget',
  label: new TranslatableMarkup('EAV widget'),
  field_types: ['eav'],
  multiple_values: TRUE,
)]
class EavWidget extends WidgetBase {

  protected EntityTypeManagerInterface $entityTypeManager;

  protected WidgetPluginManager $fieldWidgetManager;

  protected RouteMatchInterface $routeMatch;

  /**
   * {@inheritdoc}
   */
  public function __construct(
    $plugin_id,
    $plugin_definition,
    FieldDefinitionInterface $field_definition,
    array $settings,
    array $third_party_settings,
    EntityTypeManagerInterface $entity_type_manager,
    WidgetPluginManager $field_widget_manager,
    RouteMatchInterface $route_match,
  ) {
    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
    $this->entityTypeManager = $entity_type_manager;
    $this->fieldWidgetManager = $field_widget_manager;
    $this->routeMatch = $route_match;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    return new static(
      $plugin_id,
      $plugin_definition,
      $configuration['field_definition'],
      $configuration['settings'],
      $configuration['third_party_settings'],
      $container->get('entity_type.manager'),
      $container->get('plugin.manager.field.widget'),
      $container->get('current_route_match'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultSettings(): array {
    return [
      'show_in_local_task' => TRUE,
      'load_global_attributes' => TRUE,
    ] + parent::defaultSettings();
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state): array {
    $element['show_in_local_task'] = [
      '#type' => 'checkbox',
      '#title' => t('Show widget in local task'),
      '#default_value' => $this->getSetting('show_in_local_task'),
    ];

    $element['load_global_attributes'] = [
      '#type' => 'checkbox',
      '#title' => t('Show attributes without category'),
      '#default_value' => $this->getSetting('load_global_attributes'),
    ];

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state): array {
    /** @var EavItemListInterface $items */

    $host_entity = $items->getEntity();
    $field_name = $this->fieldDefinition->getName();
    $attribute_storage = $this->entityTypeManager->getStorage('eav_attribute'); /** @var EavAttributeStorage $attribute_storage */

    if ($this->getSetting('show_in_local_task') && $this->routeMatch->getRouteName() != 'entity.' . $host_entity->getEntityTypeId() . '.edit_eav') {
      $element['#access'] = FALSE;
      return $element;
    }

    $element['table'] = [
      '#type' => 'table',
      '#header' => [
        $this->t('Attribute'),
        $this->t('Value'),
        '', // For "value_id" field
      ],
      '#parents' => array_merge($element['#field_parents'], [$items->getName()]),
      '#empty' => $this->t('No available attributes.'),
      '#title' => $this->fieldDefinition->getLabel(),
      '#theme_wrappers' => !$this->getSetting('show_in_local_task') ? ['form_element'] : [],
    ];

    // Load category attributes
    $attributes = $attribute_storage->loadByHostEntityCategory($host_entity, $this->getSetting('load_global_attributes'));

    $eav_values = [];
    /** @var EavItemInterface $item */
    foreach ($items as $item) {
      if (($eav_value = $item->getValueEntity()) && ($attribute = $eav_value->getAttributeEntity())) {
        $eav_values[$attribute->getMachineName()] = $item->getValueEntity();

        // Load items attributes
        $attributes[$attribute->id()] = $attribute;
      }
    }

    // Load user attributes
    $widget_state = static::getWidgetState($element['#field_parents'], $field_name, $form_state);
    if (!empty($widget_state['custom_attributes'])) {
      $attributes += EavAttribute::loadMultiple($widget_state['custom_attributes']);
    }

    if ($attributes) {
      foreach ($attributes as $attribute) {
        // Value field need configure before EavValue::create() and before $eav_value->getValueFieldItems()
        $attribute->configureValueFieldDefinition();

        $attribute_machine_name = $attribute->getMachineName();
        $eav_value = $eav_values[$attribute_machine_name] ?? EavValue::create(['aid' => $attribute->id()]);
        $value_field_name = $attribute->getValueFieldName();
        $value_field_items = $eav_value->getValueFieldItems();

        $element['table'][$attribute_machine_name]['#eav_attribute'] = $attribute;

        $element['table'][$attribute_machine_name]['attribute'] = [
          '#markup' => $attribute->label(),
        ];

        $element['table'][$attribute_machine_name][$value_field_name] = $this->valueWidget($attribute, $value_field_items, $element, $form, $form_state);

        // @TODO Find way of pass value_id in ::massageFormValues without create table cell
        $element['table'][$attribute_machine_name]['value_id'] = [
          '#type' => 'value',
          '#value' => $eav_value->id(),
        ];
      }
    }
    else {
      $element['table']['#value'] = [];
    }

    // "Add more"
    $element['add_more'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => ['container-inline'],
      ],
    ];

    $element['add_more']['attribute'] = [
      '#type' => 'entity_autocomplete',
      '#target_type' => 'eav_attribute',
    ];

    // @TODO Add #ajax
    // @TODO Add #limit_validation_errors
    $element['add_more']['add_more_button'] = [
      '#type' => 'submit',
      '#value' => $this->t('Add attribute'),
      '#submit' => [[static::class, 'addMoreSubmit']],
    ];

    return $element;
  }

  /**
   * "Add more" button submit callback.
   */
  public static function addMoreSubmit(array $form, FormStateInterface $form_state): void {
    /** @see \Drupal\Core\Field\WidgetBase::addMoreSubmit() */
    $triggering_button = $form_state->getTriggeringElement();
    $widget_element_parents = array_slice($triggering_button['#array_parents'], 0, -2);
    $widget_element = NestedArray::getValue($form, $widget_element_parents);
    $field_name = $widget_element['#field_name'];
    $field_parents = $widget_element['#field_parents'];

    $widget_form_values = $form_state->getValue($widget_element['#parents']);
    $widget_state = static::getWidgetState($field_parents, $field_name, $form_state);
    if ($attribute_id = $widget_form_values['add_more']['attribute']) {
      $widget_state['custom_attributes'][] = $attribute_id;
      $widget_state['custom_attributes'] = array_unique($widget_state['custom_attributes']);
      static::setWidgetState($field_parents, $field_name, $form_state, $widget_state);
    }

    $form_state->setRebuild();
  }

  /**
   * {@inheritdoc}
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state): array {
    $new_values = [];

    unset($values['add_more']);

    foreach ($values as $attribute_name => $item_values) {
      $attribute = EavAttribute::loadByMachineName($attribute_name);
      $eav_value = $item_values['value_id']
        ? EavValue::load($item_values['value_id'])
        : EavValue::create(['aid' => $attribute->id()]);

      $value_field_widget = $this->fieldWidgetManager->getInstance([
        'field_definition' => $attribute->configureValueFieldDefinition(),
        'configuration' => [
          'type' => $attribute->getValueWidgetType(),
          'settings' => $attribute->getValueWidgetSettings(),
        ],
      ]);

      $value_field_items = $eav_value->getValueFieldItems();
      $value_field_form = $form[$this->fieldDefinition->getName()]['widget']['table'][$attribute_name];
      $value_field_widget->extractFormValues($value_field_items, $value_field_form, $form_state);

      if (!$value_field_items->isEmpty() || $attribute->getValueFieldDefaultValues()) {
        $eav_value->save();
        $new_values[]['entity'] = $eav_value;
      }
    }

    return $new_values;
  }

  /**
   * Return value field widget.
   */
  protected function valueWidget(EavAttributeInterface $attribute, FieldItemListInterface $value_field_items, array $element, array $form, FormStateInterface $form_state): array {
    $widget = $this->fieldWidgetManager->getInstance([
      'field_definition' => $attribute->getValueFieldDefinition(),
      'configuration' => [
        'type' => $attribute->getValueWidgetType(),
        'settings' => $attribute->getValueWidgetSettings(),
      ],
    ]);

    $form['#parents'] = array_merge($element['table']['#parents'], [$attribute->getMachineName()]);

    // @TODO Use $element without $form
    $widget_form = $widget->form($value_field_items, $form, $form_state);

    // Hide widget title
    $this->hideElementTitle($widget_form['widget']);
    foreach (Element::children($widget_form['widget']) as $key) {
      $this->hideElementTitle($widget_form['widget'][$key]);
      if (isset($widget_form['widget'][$key]['value'])) {
        $this->hideElementTitle($widget_form['widget'][$key]['value']);
      }
    }

    return $widget_form;
  }

  /**
   * Hide element title.
   */
  protected function hideElementTitle(&$element): void {
    if (isset($element['#title'])) {
      $element['#title_display'] = 'invisible';
    }
  }

}

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

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