language_negotiation_matrix-1.0.0-beta2/src/Element/Mapping.php

src/Element/Mapping.php
<?php

namespace Drupal\language_negotiation_matrix\Element;

use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\FormElement;

/**
 * Provides a mapping element.
 *
 * @FormElement("mapping")
 */
class Mapping extends FormElement {

  /**
   * Require all.
   */
  const REQUIRED_ALL = 'all';

  /**
   * Option description delimiter.
   *
   * @var string
   */
  const DESCRIPTION_DELIMITER = ' -- ';

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#input' => TRUE,
      '#process' => [
        [$class, 'processMapping'],
        [$class, 'processAjaxForm'],
      ],
      '#theme_wrappers' => ['form_element'],
      '#filter' => TRUE,
      '#required' => FALSE,
      '#source' => [],
      '#source__description_display' => 'description',
      '#destination' => [],
      '#arrow' => '→',
    ];
  }

  /**
   * Processes a likert scale form element.
   */
  public static function processMapping(&$element, FormStateInterface $form_state, &$complete_form) {
    // Set translated default properties.
    $element += [
      '#source__title' => t('Source'),
      '#destination__title' => t('Destination'),
      '#arrow' => '→',
    ];

    $arrow = htmlentities($element['#arrow']);

    // Process sources.
    $sources = [];
    foreach ($element['#source'] as $source_key => $source) {
      $source = (string) $source;
      if (!static::hasOptionDescription($source)) {
        $source_description_property_name = NULL;
        $source_title = $source;
        $source_description = '';
      }
      else {
        $source_description_property_name = ($element['#source__description_display'] === 'help') ? 'help' : 'description';
        [$source_title, $source_description] = static::splitOption($source);
      }
      $sources[$source_key] = [
        'description_property_name' => $source_description_property_name,
        'title' => $source_title,
        'description' => $source_description,
      ];
    }

    // Setup destination__type depending if #destination is defined.
    if (empty($element['#destination__type'])) {
      $element['#destination__type'] = (empty($element['#destination'])) ? 'textfield' : 'select';
    }

    // Set base destination element.
    $destination_element_base = [
      '#title_display' => 'invisible',
      '#required' => ($element['#required'] === static::REQUIRED_ALL) ? TRUE : FALSE,
      '#error_no_message'  => ($element['#required'] !== static::REQUIRED_ALL) ? TRUE : FALSE,
    ];

    // Get base #destination__* properties.
    foreach ($element as $element_key => $element_value) {
      if (strpos($element_key, '#destination__') === 0 && !in_array($element_key, ['#destination__title'])) {
        $destination_element_base[str_replace('#destination__', '#', $element_key)] = $element_value;
      }
    }

    // Build header.
    $header = [
      ['data' => ['#markup' => $element['#source__title'] . ' ' . $arrow]],
      ['data' => ['#markup' => $element['#destination__title']]],
    ];

    // Build rows.
    $rows = [];
    foreach ($sources as $source_key => $source) {
      $default_value = (isset($element['#default_value'][$source_key])) ? $element['#default_value'][$source_key] : NULL;

      // Source element.
      $source_element = ['data' => []];
      $source_element['data']['title'] = ['#markup' => $source['title']];
      if ($source['description_property_name'] === 'help') {
        $source_element['data']['help'] = [
          '#type' => 'details',
          '#description' => $source['description'],
          '#title' => $source['title'],
        ];
      }
      $source_element['data']['arrow'] = ['#markup' => $arrow, '#prefix' => ' '];
      if ($source['description_property_name'] === 'description') {
        $source_element['data']['description'] = [
          '#type' => 'container',
          '#markup' => $source['description'],
          '#attributes' => ['class' => ['description']],
        ];
      }

      // Destination element.
      $destination_element = $destination_element_base + [
          '#title' => $source['title'],
          '#required' => $element['#required'],
          '#default_value' => $default_value,
        ];

      // Apply #parents to destination element.
      if (isset($element['#parents'])) {
        $destination_element['#parents'] = array_merge($element['#parents'], [$source_key]);
      }

      switch ($element['#destination__type']) {
        case 'select':
          $destination_element += [
            '#empty_option' => t('- Select -'),
            '#options' => $element['#destination'],
          ];
          break;
      }

      // Add row.
      $rows[$source_key] = [
        'source' => $source_element,
        $source_key => $destination_element,
      ];
    }

    $element['table'] = [
        '#tree' => TRUE,
        '#type' => 'table',
        '#header' => $header,
        '#attributes' => [
          'class' => ['mapping-table'],
        ],
      ] + $rows;

    // Build table element with selected properties.
    $properties = [
      '#states',
      '#sticky',
    ];
    $element['table'] += array_intersect_key($element, array_combine($properties, $properties));

    // Add validate callback.
    $element += ['#element_validate' => []];
    array_unshift($element['#element_validate'], [get_called_class(), 'validateMapping']);

    if (!empty($element['#states'])) {
      static::processStates($element, '#wrapper_attributes');
    }

    $element['#attached']['library'][] = 'language_negotiation_matrix/language_negotiation_matrix.element.mapping';

    return $element;
  }

  /**
   * Validates a mapping element.
   */
  public static function validateMapping(&$element, FormStateInterface $form_state, &$complete_form) {
    $value = NestedArray::getValue($form_state->getValues(), $element['#parents']);

    // Filter values.
    if ($element['#filter']) {
      $value = array_filter($value);
    }

    // Note: Not validating REQUIRED_ALL because each destination element is
    // already required.
    if (Element::isVisibleElement($element)
      && $element['#required']
      && $element['#required'] !== static::REQUIRED_ALL
      && empty($value)) {
      static::setRequiredError($element, $form_state);
    }

    $element['#value'] = $value;
    $form_state->setValueForElement($element, $value);
  }

  /**
   * Set form state required error for a specified element.
   *
   * @param array $element
   *   An element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param string $title
   *   OPTIONAL. Required error title.
   */
  public static function setRequiredError(array $element, FormStateInterface $form_state, $title = NULL) {
    if (isset($element['#required_error'])) {
      $form_state->setError($element, $element['#required_error']);
    }
    elseif ($title) {
      $form_state->setError($element, t('@name field is required.', ['@name' => $title]));
    }
    elseif (isset($element['#title'])) {
      $form_state->setError($element, t('@name field is required.', ['@name' => $element['#title']]));
    }
    else {
      $form_state->setError($element);
    }
  }

  /**
   * Adds JavaScript to change the state of an element based on another element.
   *
   * @param array $elements
   *   A renderable array element having a #states property as described above.
   * @param string $key
   *   The element property to add the states attribute to.
   *
   * @see \Drupal\Core\Form\FormHelper::processStates
   */
  public static function processStates(array &$elements, $key = '#attributes') {
    if (empty($elements['#states'])) {
      return;
    }

    $elements['#attached']['library'][] = 'core/drupal.states';
    $elements[$key]['data-drupal-states'] = Json::encode($elements['#states']);
    // Make sure to include target class for this container.
    if (empty($elements[$key]['class']) || !static::inArray(['js-form-item', 'js-form-submit', 'js-form-wrapper'], $elements[$key]['class'])) {
      $elements[$key]['class'][] = 'js-form-item';
    }
  }

  /**
   * Determine if any values are in an array.
   *
   * @param array $needles
   *   The searched values.
   * @param array $haystack
   *   The array.
   *
   * @return bool
   *   TRUE if any values are in an array.
   *
   * @see http://stackoverflow.com/questions/7542694/in-array-multiple-values
   */
  public static function inArray(array $needles, array $haystack) {
    return !!array_intersect($needles, $haystack);
  }

  /**
   * Determine if option text includes a description.
   *
   * @param string $text
   *   Option text.
   *
   * @return bool
   *   TRUE option text includes a description.
   */
  public static function hasOptionDescription($text) {
    return (strpos($text, static::DESCRIPTION_DELIMITER) !== FALSE) ? TRUE : FALSE;
  }

  /**
   * Split option text into an array containing an option's text and description.
   *
   * @param string $text
   *   Option text.
   *
   * @return array
   *   An array containing an option's text and description.
   */
  public static function splitOption($text) {
    return explode(static::DESCRIPTION_DELIMITER, $text);
  }
}

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

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