module_builder-8.x-3.x-dev/src/Form/ModuleHooksForm.php

src/Form/ModuleHooksForm.php
<?php

namespace Drupal\module_builder\Form;

use Drupal\Component\Utility\Html;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\module_builder\ExceptionHandler;
use DrupalCodeBuilder\Exception\SanityException;
use MutableTypedData\Data\DataItem;

/**
 * Form for selecting hooks.
 */
class ModuleHooksForm extends ComponentSectionForm {

  /**
   * {@inheritdoc}
   */
  protected function getFormComponentProperties(DataItem $data) {
    $properties = parent::getFormComponentProperties($data);

    // Remove the 'hooks' property, as this form handles it directly. It has to
    // be declared in the entity annotation so that the 'misc' form doesn't pick
    // it up.
    $key = array_search('hooks', $properties);
    if ($key) {
      unset($properties[$key]);
    }

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);

    // $entity_component_data = $this->getComponentDataObject();
    $entity_component_data = $form_state->get('data');

    $form['hooks'] = [
      '#type' => 'container',
    ];

    $procedural_hooks_element = [
      '#type' => 'container',
    ];

    $procedural_hooks_element['filter'] = [
      '#type' => 'search',
      '#title' => $this->t('Filter'),
      '#title_display' => 'invisible',
      '#size' => 60,
      '#placeholder' => $this->t('Filter by hook name'),
      '#attributes' => [
        'class' => ['hooks-filter-text'],
        'data-container' => '.module-builder-hooks',
        'autocomplete' => 'off',
        'title' => $this->t('Enter a part of the hook name to filter by.'),
      ],
    ];

    // Get the Task handler.
    // No need to catch DCB exceptions; create() has already done that.
    // TODO: inject.
    $mb_task_handler_report = \Drupal::service('module_builder.drupal_code_builder')->getTask('ReportHookData');

    // Call a method in the Task handler to perform the operation.
    $hook_info = $mb_task_handler_report->listHookOptionsStructured();

    $procedural_hooks_element['groups'] = [
      '#type' => 'container',
      '#attributes' => ['class' => ['module-builder-hooks']],
    ];

    // Create a fieldset for each group, containing checkboxes.
    foreach ($hook_info as $group => $hook_group_info) {
      $procedural_hooks_element['groups'][$group] = array(
        '#type' => 'details',
        '#title' => $group,
        //'#open' => TRUE,
      );

      $hook_names = array_keys($hook_group_info);

      // Need to differentiate the key, otherwise FormAPI treats this as an
      // error on submit.
      $group_default_value = isset($entity_component_data['hooks']) ? array_intersect($hook_names, $entity_component_data['hooks']) : [];
      $procedural_hooks_element['groups'][$group][$group . '_hooks'] = array(
        '#type' => 'checkboxes',
        '#options' => array_combine($hook_names, array_column($hook_group_info, 'name')),
        '#default_value' => $group_default_value,
      );

      if (!empty($group_default_value)) {
        $procedural_hooks_element['groups'][$group]['#open'] = TRUE;
      }

      foreach ($hook_group_info as $hook => $hook_info_single) {
        $description = $hook_info_single['description'];

        if ($hook_info_single['core']) {
          // Get MAJOR, MINOR and PATCH components from the current version
          // string.
          [$major, $minor, $patch] = explode('.', \Drupal::VERSION);

          // Documentation URLs on api.drupal.org changed from 9 onwards.
          if ($major >= 9) {
            // MAJOR version string.
            $url_version_suffix = $major;
          }
          else {
            // MAJOR.MINOR.x version string.
            $url_version_suffix = implode('.', [$major, $minor, 'x']);
          }

          // External Uri.
          $url = Url::fromUri('https://api.drupal.org/api/function/' . $hook . '/' . $url_version_suffix);
          $description .= ' ' . Link::fromTextAndUrl(t('[documentation]'), $url)->toString();
        }

        $procedural_hooks_element['groups'][$group][$group . '_hooks'][$hook]['#description'] = $description;
      }
    }

    $form['hooks']['procedural_hooks'] = $procedural_hooks_element;

    $form_state->set('module_builder_groups', array_keys($hook_info));

    $form['#attached']['library'][] = 'module_builder/hooks';

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  protected function buildComplexFormElement(array $element, FormStateInterface $form_state, DataItem $data): array {
    $element = parent::buildComplexFormElement($element, $form_state, $data);

    // Streamline hook implementation mutable data elements.
    // Remove the notice about 'no additional properties' as it's quite noisy
    // for hooks.
    unset($element['count_notice']);
    // Remove the button to update variants, as it takes up a lot of space and
    // it's just as quick to remove the hook and add a new one.
    unset($element[':update_variant']);

    return $element;
  }

  /**
   * {@inheritdoc}
   */
  protected function buildMultipleDeltaFormElement(array $element, FormStateInterface $form_state, DataItem $data): array {
    if ($data->getName() == 'hook_methods') {
      return $this->builderHookMethodsTableFormElement($element, $form_state, $data);
    }
    else {
      return parent::buildMultipleDeltaFormElement($element, $form_state, $data);
    }
  }

  /**
   * Builds a table form element for the hook_methods property.
   */
  protected function builderHookMethodsTableFormElement(array $element, FormStateInterface $form_state, DataItem $data): array {
    // Set up a wrapper for AJAX.
    $wrapper_id = Html::getId($data->getAddress() . '-add-more-wrapper');

    $element += [
      '#type' => 'table',
      '#caption' => $data->getDescription(),
      '#attributes' => [
        'id' => $wrapper_id,
        'class' => ['hook_methods'],
      ],
      '#header' => [
        $this->t('Hook'),
        $this->t('Token replacements'),
        $this->t('Actions'),
      ],
    ];

    $element['#empty'] = $this->t('There are no hook methods in this class. Add one by selecting a hook below.');

    foreach ($data as $delta => $delta_item) {
      $element[$delta] = [];
      $element[$delta]['hook_name'] = [
        '#type' => 'value',
        '#value' => $delta_item->hook_name->value,
      ];

      // This has to go inside the hidden value element, as otherwise the
      // table element will treat the two as different cells.
      // See https://www.drupal.org/project/drupal/issues/3520847.
      $element[$delta]['hook_name'][':dummy_hook_name'] = [
        '#markup' => $delta_item->hook_name->value,
      ];

      if ($delta_item->hasProperty('hook_name_parameters')) {
        $element[$delta]['hook_name_parameters'] = [
          '#type' => 'textfield',
          '#default_value' => implode(', ', $delta_item->hook_name_parameters->values()),
          '#attributes' => [
            'placeholder' => $this->t('Separate multiple tokens with a comma'),
          ],
        ];
      }
      else {
        $element[$delta][':empty'] = [];
      }

      $element[$delta][':remove_button'] = [
        '#type' => 'submit',
        // Needs to be full address for uniquess in the whole form.
        '#name' => $delta_item->getAddress() . '_remove_item',
        '#data_address' => $delta_item->getAddress(),
        '#ajax_parent_slice' => 2,
        '#value' => $this->t('Remove hook method @human-index', [
          '@human-index' => $delta + 1,
        ]),
        '#limit_validation_errors' => [],
        '#submit' => ['::removeItemSubmit'],
        '#ajax' => [
          'callback' => '::itemButtonAjax',
          'wrapper' => $wrapper_id,
          'effect' => 'fade',
        ],
      ];
    }

    if ($data->mayAddItem()) {
      $element = $this->buildAddItemButton($element, $form_state, $data);

      $element[':variant_add'][':type_property']['#wrapper_attributes'] = ['colspan' => 2];
      $element[':variant_add'][':add_button']['#value'] = $this->t('Add hook method');
    }

    return $element;
  }

  /**
   * Copies top-level form values to entity properties
   *
   * This should not change existing entity properties that are not being edited
   * by this form.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   The entity the current form should operate upon.
   * @param array $form
   *   A nested array of form elements comprising the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  protected function copyFormValuesToEntity(EntityInterface $entity, array $form, FormStateInterface $form_state) {
    // Call the parent to handle all values that are beneath 'module'.
    parent::copyFormValuesToEntity($entity, $form, $form_state);

    // Handle the 'hooks' values separately.
    $data = $form_state->get('data');
    $entity_data_values = $entity->get('data');
    $form_values = $form_state->getValues();

    // We can't just iterate over $form_values, because of a core bug with
    // EntityForm, so we need to know which keys to look at.
    // See https://www.drupal.org/node/2665714.
    $groups = $form_state->get('module_builder_groups');

    $hooks = [];
    foreach ($groups as $group) {
      $group_values = $form_values[$group . '_hooks'];
      // Filter out empty values. (FormAPI *still* doesn't do this???)
      $group_hooks = array_filter($group_values);
      // Store as a numeric array.
      $group_hooks = array_keys($group_hooks);

      $hooks = array_merge($group_hooks, $hooks);;
    }

    $entity_data_values['hooks'] = $hooks;

    $entity->set('data', $entity_data_values);
  }

}

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

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