custom_field-1.0.x-dev/src/Plugin/CustomField/FieldWidget/LinkWidget.php

src/Plugin/CustomField/FieldWidget/LinkWidget.php
<?php

declare(strict_types=1);

namespace Drupal\custom_field\Plugin\CustomField\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\custom_field\Attribute\CustomFieldWidget;
use Drupal\custom_field\LinkAttributesManager;
use Drupal\custom_field\Plugin\CustomField\FieldType\LinkType;
use Drupal\custom_field\Plugin\CustomFieldTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Plugin implementation of the 'url' widget.
 */
#[CustomFieldWidget(
  id: 'link_default',
  label: new TranslatableMarkup('Link'),
  category: new TranslatableMarkup('Url'),
  field_types: [
    'link',
  ],
)]
class LinkWidget extends UrlWidgetBase {

  /**
   * The link attributes manager.
   *
   * @var \Drupal\custom_field\LinkAttributesManager
   */
  protected LinkAttributesManager $linkAttributesManager;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->linkAttributesManager = $container->get('plugin.manager.custom_field_link_attributes');

    return $instance;
  }

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

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

    $element['placeholder_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Placeholder for URL'),
      '#default_value' => $settings['placeholder_url'],
      '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
    ];
    $element['placeholder_title'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Placeholder for link text'),
      '#default_value' => $settings['placeholder_title'],
      '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
      '#access' => $field->getFieldSetting('title') != DRUPAL_DISABLED,
    ];

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function widget(FieldItemListInterface $items, int $delta, array $element, array &$form, FormStateInterface $form_state, CustomFieldTypeInterface $field): array {
    $element = parent::widget($items, $delta, $element, $form, $form_state, $field);
    $field_definition = $items->getFieldDefinition();
    $item = $items[$delta];
    $field_settings = $field->getFieldSettings();
    $settings = $this->getSettings() + static::defaultSettings();
    $subfield = $field->getName();
    $options = $item->get($subfield . '__options')->getValue() ?? [];
    $attributes = $options['attributes'] ?? [];

    // Overrides for this widget.
    $element['uri']['#title'] = $this->t('URL');
    $element['uri']['#placeholder'] = $settings['placeholder_url'];

    // Make uri required on the front-end when title filled-in.
    if (!$this->isDefaultValueWidget($form_state) && $field_settings['title'] !== DRUPAL_DISABLED && !$element['uri']['#required']) {
      $parents = $element['#field_parents'] ?? [];
      $parents[] = $field_definition->getName();
      $selector = $root = array_shift($parents);
      if ($parents) {
        $selector = $root . '[' . implode('][', $parents) . ']';
      }

      $element['uri']['#states']['required'] = [
        ':input[name="' . $selector . '[' . $delta . '][' . $subfield . '][title]"]' => ['filled' => TRUE],
      ];
    }

    $element['title'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Link text'),
      '#placeholder' => $settings['placeholder_title'],
      '#default_value' => $item->get($subfield . '__title')->getValue() ?? NULL,
      '#maxlength' => 255,
      '#access' => $field_settings['title'] != DRUPAL_DISABLED,
      '#required' => $field_settings['title'] === DRUPAL_REQUIRED && $element['#required'],
    ];
    // Post-process the title field to make it conditionally required if URL is
    // non-empty. Omit the validation on the field edit form, since the field
    // settings cannot be saved otherwise.
    //
    // Validate that title field is filled out (regardless of uri) when it is a
    // required field.
    if (!$this->isDefaultValueWidget($form_state) && $field_settings['title'] === DRUPAL_REQUIRED) {
      $element['#element_validate'][] = [static::class, 'validateTitleElement'];
      $element['#element_validate'][] = [static::class, 'validateTitleNoLink'];

      if (!$element['title']['#required']) {
        // Make title required on the front-end when URI filled-in.
        $parents = $element['#field_parents'];
        $parents[] = $field_definition->getName();
        $selector = $root = array_shift($parents);
        if ($parents) {
          $selector = $root . '[' . implode('][', $parents) . ']';
        }

        $element['title']['#states']['required'] = [
          ':input[name="' . $selector . '[' . $delta . '][' . $subfield . '][uri]"]' => ['filled' => TRUE],
        ];
      }
    }

    // Ensure that a URI is always entered when an optional title field is
    // submitted.
    if (!$this->isDefaultValueWidget($form_state) && $field_settings['title'] == DRUPAL_OPTIONAL) {
      $element['#element_validate'][] = [static::class, 'validateTitleNoLink'];
    }

    if (!empty(array_filter($field_settings['enabled_attributes']))) {
      $widget_default_open_setting = $field_settings['widget_default_open'];

      $open = NULL;
      match ($widget_default_open_setting) {
        LinkType::WIDGET_OPEN_EXPANDED => $open = TRUE,
        LinkType::WIDGET_OPEN_COLLAPSED => $open = FALSE,
        default => $open = count($attributes),
      };

      $element['options']['attributes'] = [
        '#type' => 'details',
        '#title' => $this->t('Attributes'),
        '#tree' => TRUE,
        '#open' => $open,
      ];
      $required = FALSE;
      $plugin_definitions = $this->linkAttributesManager->getDefinitions();
      foreach (array_keys(array_filter($field_settings['enabled_attributes'])) as $attribute) {
        if (isset($plugin_definitions[$attribute])) {
          foreach ($plugin_definitions[$attribute] as $property => $value) {
            if ($property === 'id') {
              // Don't set ID.
              continue;
            }
            $element['options']['attributes'][$attribute]['#' . $property] = $value;
          }
          // Set the default value, in case of a class that is stored as array,
          // convert it back to a string.
          $default_value = $attributes[$attribute] ?? NULL;
          if ($attribute === 'class' && is_array($default_value)) {
            $default_value = implode(' ', $default_value);
          }
          if (isset($default_value)) {
            $element['options']['attributes'][$attribute]['#default_value'] = $default_value;
          }
          $required = $required || !empty($element['options']['attributes'][$attribute]['#required']);
        }
      }
      // Open the widget by default if there is a required attribute.
      $element['options']['attributes']['#open'] = $element['options']['attributes']['#open'] || $required;
    }
    // Wrap everything in a details' element.
    if ($field_settings['title'] != DRUPAL_DISABLED || !empty(array_filter($field_settings['enabled_attributes']))) {
      $element += [
        '#type' => 'fieldset',
      ];
    }
    else {
      $element += [
        '#type' => 'container',
      ];
      // If we're only showing the uri field, replace the uri title with the
      // element title.
      $element['uri']['#title'] = $element['#title'];
      if (!empty($element['#description'])) {
        // If we have the description of the type of field together with
        // the user provided description, we want to make a distinction
        // between "core help text" and "user entered help text". To make
        // this distinction more clear, we put them in an unordered list.
        $element['uri']['#description'] = [
          '#theme' => 'item_list',
          '#items' => [
            // Assume the user-specified description has the most relevance,
            // so place it first.
            $element['#description'],
            $element['uri']['#description'],
          ],
        ];
      }
    }

    if ($element['#type'] === 'fieldset') {
      // Force the fieldset description to logical position.
      $element['#description_display'] = 'before';
    }

    return $element;
  }

  /**
   * Form element validation handler for the 'title' element.
   *
   * Conditionally requires the link title if a URL value was filled in.
   *
   * @param array<string, mixed> $element
   *   The form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array<string, mixed> $form
   *   The form.
   */
  public static function validateTitleElement(array &$element, FormStateInterface $form_state, array $form): void {
    if ($element['uri']['#value'] !== '' && $element['title']['#value'] === '') {
      // We expect the field name placeholder value to be wrapped in $this->t()
      // here, so it won't be escaped again as it's already marked safe.
      $form_state->setError($element['title'], new TranslatableMarkup('@title field is required if there is @uri input.', [
        '@title' => $element['title']['#title'],
        '@uri' => $element['uri']['#title'],
      ]));
    }
  }

  /**
   * Form element validation handler for the 'title' element.
   *
   * Requires the URL value if a link title was filled in.
   *
   * @param array<string, mixed> $element
   *   The form element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array<string, mixed> $form
   *   The form.
   */
  public static function validateTitleNoLink(array &$element, FormStateInterface $form_state, array $form): void {
    if ($element['uri']['#value'] === '' && $element['title']['#value'] !== '') {
      $form_state->setError($element['uri'], new TranslatableMarkup('The @uri field is required when the @title field is specified.', [
        '@title' => $element['title']['#title'],
        '@uri' => $element['uri']['#title'],
      ]));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function massageFormValue(mixed $value, $column): ?array {
    if (empty($value['uri'])) {
      return NULL;
    }
    $value['uri'] = static::getUserEnteredStringAsUri($value['uri']);
    $value += ['options' => []];
    if (isset($value['options']['attributes'])) {
      $attributes = $value['options']['attributes'];
      $value['options']['attributes'] = array_filter($attributes, function ($attribute) {
        return $attribute !== "";
      });
      // Convert a class string to an array so that it can be merged reliable.
      if (isset($value['options']['attributes']['class']) && is_string($value['options']['attributes']['class'])) {
        $value['options']['attributes']['class'] = explode(' ', $value['options']['attributes']['class']);
      }
    }

    return $value;
  }

}

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

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