custom_field-1.0.x-dev/modules/custom_field_viewfield/src/Plugin/CustomField/FieldWidget/ViewfieldSelectWidget.php

modules/custom_field_viewfield/src/Plugin/CustomField/FieldWidget/ViewfieldSelectWidget.php
<?php

declare(strict_types=1);

namespace Drupal\custom_field_viewfield\Plugin\CustomField\FieldWidget;

use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Field\FieldFilteredMarkup;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\custom_field\Attribute\CustomFieldWidget;
use Drupal\custom_field\Plugin\CustomFieldTypeInterface;
use Drupal\custom_field\Plugin\CustomFieldWidgetBase;
use Drupal\custom_field_viewfield\Plugin\CustomField\FieldType\ViewfieldType;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Plugin implementation of the 'viewfield_select' widget.
 */
#[CustomFieldWidget(
  id: 'viewfield_select',
  label: new TranslatableMarkup('Viewfield select'),
  category: new TranslatableMarkup('Viewfield'),
  field_types: [
    'viewfield',
  ],
)]
class ViewfieldSelectWidget extends CustomFieldWidgetBase {

  /**
   * The token entity mapper service.
   *
   * @var \Drupal\token\TokenEntityMapperInterface|null
   */
  protected mixed $tokenEntityMapper;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->tokenEntityMapper = $container->get(
      'token.entity_mapper',
      ContainerInterface::NULL_ON_INVALID_REFERENCE
    );

    return $instance;
  }

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

  /**
   * {@inheritdoc}
   */
  public function widgetSettingsForm(FormStateInterface $form_state, CustomFieldTypeInterface $field): array {
    $element = parent::widgetSettingsForm($form_state, $field);
    $settings = $this->getSettings() + static::defaultSettings();

    $element['empty_option'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Empty option'),
      '#description' => $this->t('Option to show when field is not required.'),
      '#default_value' => $settings['empty_option'],
      '#required' => TRUE,
    ];

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function widget(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state, CustomFieldTypeInterface $field): array {
    $element = parent::widget($items, $delta, $element, $form, $form_state, $field);
    assert($field instanceof ViewfieldType);
    $settings = $this->getSettings() + static::defaultSettings();
    $field_parents = $element['#field_parents'];
    $field_settings = $field->getFieldSettings();
    $allowed_views = $this->getAllowedViewsOptions($field_settings['allowed_views']);
    $token_module_installed = $this->moduleHandler->moduleExists('token');
    /** @var \Drupal\custom_field\Plugin\Field\FieldType\CustomItem $item */
    $item = $items[$delta];
    $entity_type_id = $item->getEntity()->getEntityTypeId();
    $is_required = $item->getFieldDefinition()->isRequired() && $field_settings['required'];
    $values = $form_state->getValues();
    if ($this->isDefaultValueWidget($form_state) && !$field_settings['force_default']) {
      $is_required = FALSE;
    }
    $field_name = $item->getFieldDefinition()->getName();
    $name = $field->getName();
    if (!$this->isDefaultValueWidget($form_state) && $field_settings['force_default']) {
      $element['#access'] = FALSE;
    }
    $wrapper = $this->getUniqueElementId($form, $field_name, $delta, $name);

    // Create a condition string for states api.
    $path_parts = [...$field_parents];
    $base = array_shift($path_parts);
    $visibility_path = $base . '[' . implode('][', $path_parts) . ']';

    $field_value = NestedArray::getValue($values, $field_parents);
    // If there are no processed values, use the input.
    if (empty($field_value)) {
      $user_input = $form_state->getUserInput();
      $field_value = NestedArray::getValue($user_input, $field_parents);
    }
    if (!empty($field_value) && isset($field_value['target_id'])) {
      $target_id = $field_value['target_id'];
      $default_display_id = $field_value['display_id'];
      $default_arguments = $field_value['view_options']['arguments'];
      $default_items_to_display = $field_value['view_options']['items_to_display'];
    }
    // Use the saved values.
    else {
      $target_id = $item->{$name};
      $default_display_id = $item->{$name . '__display'} ?? NULL;
      $default_arguments = $item->{$name . '__arguments'} ?? NULL;
      $default_items_to_display = $item->{$name . '__items'} ?? NULL;
    }

    // Use the allowed displays by the current view selected.
    $display_id_options = $target_id ? $allowed_views[$target_id]['displays'] ?? [] : [];

    // Add a container div for flex layout compatibility.
    $element['#theme_wrappers'] = ['container'];
    // Add our widget type and additional properties and return.
    $element['target_id'] = [
      '#title' => $this->t('View'),
      '#type' => 'select',
      '#options' => $field->getViewOptions(TRUE),
      '#empty_value' => '',
      '#default_value' => $target_id,
      '#description' => $this->t('View name.'),
      '#required' => $is_required,
      '#ajax' => [
        'callback' => [$this, 'ajaxGetDisplayOptions'],
        'wrapper' => $wrapper,
        'progress' => [
          'type' => 'throbber',
          'message' => $this->t('Getting options...'),
        ],
      ],
    ];
    if (!$is_required) {
      $element['target_id']['#empty_option'] = $settings['empty_option'];
    }

    $element['display_id'] = [
      '#title' => $this->t('Display'),
      '#type' => 'select',
      '#options' => $display_id_options,
      '#empty_option' => $settings['empty_option'],
      '#default_value' => $default_display_id,
      '#description' => $this->t('View display to be used.'),
      '#prefix' => '<div id="' . $wrapper . '">',
      '#suffix' => '</div>',
    ];

    // Hide the display id field and set its value to NULL if no view selected.
    if (count($display_id_options) < 1) {
      $element['display_id']['#type'] = 'hidden';
      $element['display_id']['#value'] = NULL;
      unset($element['display_id']['#options']);
    }

    else {
      // Set value for display_id back to NULL when options change.
      if (!empty($default_display_id) && !array_key_exists($default_display_id, $display_id_options)) {
        $element['display_id']['#value'] = NULL;
      }
      $element['display_id']['#required'] = TRUE;
    }

    $element['view_options'] = [
      '#type' => 'details',
      '#title' => $this->t('Advanced options'),
      '#states' => [
        'visible' => [
          [':input[name="' . $visibility_path . '[target_id]"]' => ['filled' => TRUE]],
        ],
      ],
    ];
    $element['view_options']['arguments'] = [
      '#title' => $this->t('Arguments'),
      '#type' => 'textfield',
      '#default_value' => $default_arguments ?? NULL,
      '#description' => Markup::create($this->t('Separate contextual filters with a "/". Each filter may use "+" or "," for multi-value arguments.<br> @tokens', [
        '@tokens' => $token_module_installed ? $this->t('This field supports tokens.') : '',
      ])),
      '#maxlength' => 255,
    ];
    $element['view_options']['items_to_display'] = [
      '#title' => $this->t('Items to display'),
      '#type' => 'number',
      '#default_value' => $default_items_to_display,
      '#description' => $this->t('Override the number of items to display. This also disables the pager if one is configured. Leave empty for default limit.'),
      '#min' => 1,
      '#max' => 100,
    ];
    if ($token_module_installed) {
      $token_type = $this->tokenEntityMapper->getTokenTypeForEntityType($entity_type_id);
      $element['view_options']['token_help'] = [
        '#theme' => 'token_tree_link',
        '#token_types' => [$token_type],
        '#recursion_limit' => $field_settings['token_browser']['recursion_limit'] ?? 3,
        '#recursion_limit_max' => 6,
        '#global_types' => $field_settings['token_browser']['global_types'] ?? FALSE,
      ];
    }

    return $element;
  }

  /**
   * Get allowed views for widget options.
   *
   * @param array<string, mixed> $allowed_views
   *   An array of views to filter by.
   *
   * @return array<string, mixed>
   *   A filtered array of views based on enabled displays.
   */
  public function getAllowedViewsOptions(array $allowed_views): array {
    $views = Views::getEnabledViews();
    $allowed_options = [];

    foreach ($allowed_views as $view_name => $displays) {
      // Check if the view and our filters exist in $views before adding.
      if (isset($views[$view_name])) {
        $filtered_displays = array_filter($displays);
        $display_options = [];
        foreach ($filtered_displays as $key => $display) {
          $views_display = $views[$view_name]->getDisplay($key);
          if (isset($views_display['display_options']['enabled']) && !$views_display['display_options']['enabled']) {
            continue;
          }
          $display_options[$key] = FieldFilteredMarkup::create($views_display['display_title']);
        }
        if (!empty($display_options)) {
          natcasesort($display_options);
          $allowed_options[$view_name] = [
            'label' => $views[$view_name]->label(),
            'displays' => $display_options,
          ];
        }
      }
    }

    return $allowed_options;
  }

  /**
   * Ajax callback to retrieve display IDs.
   *
   * @param array $form
   *   The form from which the display IDs are being requested.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state of the form.
   *
   * @return \Drupal\Core\Ajax\AjaxResponse
   *   The Ajax response.
   */
  public function ajaxGetDisplayOptions(array &$form, FormStateInterface $form_state): AjaxResponse {
    $trigger = $form_state->getTriggeringElement();
    $wrapper_id = $trigger['#ajax']['wrapper'];
    $form_state_keys = array_slice($trigger['#array_parents'], 0, -1);

    // Get the updated element from the form structure.
    $updated_element = NestedArray::getValue($form, $form_state_keys)['display_id'];
    $sliced_parents = array_slice($trigger['#parents'], 0, -1, TRUE);
    $user_input = $form_state->getUserInput();

    NestedArray::setValue($user_input, [...$sliced_parents, 'display_id'], NULL);
    $form_state->setValueForElement($updated_element, NULL);
    $form_state->setUserInput($user_input);

    $response = new AjaxResponse();
    // Add a ReplaceCommand to replace the content inside the widget's wrapper.
    $response->addCommand(new ReplaceCommand('#' . $wrapper_id, $updated_element));
    $form_state->setRebuild();

    return $response;
  }

  /**
   * {@inheritdoc}
   */
  public function massageFormValue(mixed $value, array $column): mixed {
    if (isset($value['target_id']) && empty($value['target_id'])) {
      return NULL;
    }
    if (isset($value['view_options'])) {
      $value = array_merge($value, $value['view_options']);
      unset($value['view_options']);
    }
    return $value;
  }

}

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

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