You are here

Select2Boxes.php in Select2 Boxes 8

File

modules/select2_bef/src/Plugin/better_exposed_filters/filter/Select2Boxes.php
View source
<?php

namespace Drupal\select2_bef\Plugin\better_exposed_filters\filter;

use Drupal\better_exposed_filters\Plugin\better_exposed_filters\filter\FilterWidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Plugin\views\filter\InOperator;

/**
 * Default widget implementation.
 *
 * @BetterExposedFiltersFilterWidget(
 *   id = "select2boxes",
 *   label = @Translation("Select2 boxes"),
 * )
 */
class Select2Boxes extends FilterWidgetBase {

  /**
   * {@inheritdoc}
   */
  public static function isApplicable($filter = NULL, array $filter_options = []) {
    if ($filter instanceof InOperator) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return parent::defaultConfiguration() + [
      'advanced' => [
        'limited_search' => FALSE,
        'minimum_search_length' => '',
        'include_flags' => FALSE,
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {

    /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */
    $filter = $this->handler;
    $filter_id = $this
      ->getExposedFilterFieldId();
    $form = parent::buildConfigurationForm($form, $form_state);
    if (empty($filter->options['expose']['multiple'])) {
      $form['advanced']['limited_search'] = [
        '#type' => 'checkbox',
        '#title' => $this
          ->t('Limit search box visibility by list length'),
        '#default_value' => !empty($this->configuration['advanced']['limited_search']),
        '#weight' => 10,
      ];
      $form['advanced']['minimum_search_length'] = [
        '#type' => 'textfield',
        '#title' => $this
          ->t('Minimum list length'),
        '#default_value' => $this->configuration['advanced']['minimum_search_length'],
        '#weight' => 11,
        '#states' => [
          'visible' => [
            [
              ':input[name="exposed_form_options[bef][filter][' . $filter_id . '][configuration][advanced][limited_search]"]' => [
                'checked' => TRUE,
              ],
            ],
          ],
        ],
      ];
    }

    // Include the flags icons if available.
    $this
      ->addIncludeIconsOption($form, $filter);
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function exposedFormAlter(array &$form, FormStateInterface $form_state) {

    /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */
    $filter = $this->handler;
    $filter_id = $this
      ->getExposedFilterFieldId();
    parent::exposedFormAlter($form, $form_state);
    $form[$filter_id]['#type'] = 'select';
    $form[$filter_id]['#attributes']['class'][] = 'select2-widget';
    $form[$filter_id]['#attributes']['data-jquery-once-autocomplete'] = 'true';
    $form[$filter_id]['#attributes']['data-select2-autocomplete-list-widget'] = 'true';

    // Data attributes depending on if multiple values are allowed.
    if (empty($filter->options['expose']['multiple'])) {
      $form[$filter_id]['#multiple'] = FALSE;
      if (!empty($this->configuration['advanced']['limited_search'])) {
        $form[$filter_id]['#attributes']['data-minimum-search-length'] = $this->configuration['advanced']['minimum_search_length'];
      }
    }
    else {
      $form[$filter_id]['#attributes']['data-select2-multiple'] = 'true';
      $form[$filter_id]['#multiple'] = TRUE;
    }

    // Include the flags icons if enabled in BEF settings.
    if (!empty($this->configuration['advanced']['include_flags'])) {
      $this
        ->includeIcons($form, $filter->definition, $filter_id);
    }

    // Workaround to add support for merging process and pre-render functions
    // to the render array of an element.
    // @todo remove once core issue is resolved.
    // @see https://www.drupal.org/project/drupal/issues/2070131
    $form[$filter_id]['#process'][] = [
      '\\Drupal\\Core\\Render\\Element\\Select',
      'processSelect',
    ];
    $form[$filter_id]['#process'][] = [
      '\\Drupal\\Core\\Render\\Element\\Select',
      'processAjaxForm',
    ];
    $form[$filter_id]['#pre_render'][] = [
      '\\Drupal\\Core\\Render\\Element\\Select',
      'preRenderSelect',
    ];

    // Attach the JS.
    $form['#attached']['library'][] = 'select2boxes/widget';
  }

  /**
   * Include flags icons.
   *
   * @param array &$form
   *   Form array.
   * @param \Drupal\views\Plugin\views\filter\FilterPluginBase $filter
   *   View filter object.
   */
  protected function addIncludeIconsOption(array &$form, FilterPluginBase $filter) {

    // Check if the field is of allowed type to include flags icons.
    $applicable_fields = [
      'Drupal\\views\\Plugin\\views\\filter\\LanguageFilter',
      'Drupal\\country\\Plugin\\views\\filter\\CountryItem',
      'Drupal\\address\\Plugin\\views\\filter\\Country',
    ];
    foreach ($applicable_fields as $field) {
      if (is_a($filter, $field)) {
        if (\Drupal::moduleHandler()
          ->moduleExists('flags')) {

          // Add a new option to the settings form.
          $form['advanced']['include_flags'] = [
            '#type' => 'checkbox',
            '#title' => $this
              ->t('Include flags icons'),
            '#default_value' => $this->configuration['advanced']['include_flags'],
          ];
        }
      }
    }
  }

  /**
   * Include flags icons.
   *
   * @param array &$form
   *   Form array.
   * @param array $filter_definition
   *   Filter's definition data.
   * @param string $key
   *   Element's key.
   */
  protected static function includeIcons(array &$form, array $filter_definition, $key) {

    // Create a map of country or language dependent classes.
    $flags = [];

    // Get some additional data from the filter's definition data array.
    $field_name = isset($filter_definition['field_name']) ? $filter_definition['field_name'] : $filter_definition['entity field'];
    $entity_type = $filter_definition['entity_type'];

    // Get the field's definition object to know the field's type.

    /** @var \Drupal\Core\Field\FieldDefinitionInterface $definition */
    $definition = \Drupal::service('entity_field.manager')
      ->getFieldStorageDefinitions($entity_type)[$field_name];

    // Use an appropriate mapper service relatively to the field's type.
    $mapper = !is_null($definition) && $definition
      ->getType() == 'country' ? \Drupal::service('flags.mapping.country') : \Drupal::service('flags.mapping.language');
    foreach (array_keys($form[$key]['#options']) as $key) {
      if ($key == '***LANGUAGE_language_interface***' || $key == '***LANGUAGE_site_default***') {
        continue;
      }
      $flags[$key] = [
        'flag',
        'flag-' . $mapper
          ->map($key),
        $mapper
          ->getExtraClasses()[0],
      ];
    }

    // Merge these values to have all countries
    // and languages flags in the same place to prevent missing flags icons.
    if (!isset($form['#attached']['drupalSettings']['flagsClasses'])) {
      $form['#attached']['drupalSettings']['flagsClasses'] = [];
    }
    $form['#attached']['drupalSettings']['flagsClasses'] += $flags;

    // We have to use field's column name for views,
    // e.g. FIELD_NAME_value instead of FIELD_NAME.
    // Note: small workaround for the entity properties like langcode.
    if (!isset($filter_definition['field'])) {
      $filter_definition['field'] = $filter_definition['entity field'];
    }
    $form['#attached']['drupalSettings']['flagsFields'][$filter_definition['field']] = TRUE;

    // Attach the flags library.
    $form['#attached']['library'][] = 'flags/flags';
  }

}

Classes

Namesort descending Description
Select2Boxes Default widget implementation.