class RearrangeFilter

Same name and namespace in other branches
  1. 10 core/modules/views_ui/src/Form/Ajax/RearrangeFilter.php \Drupal\views_ui\Form\Ajax\RearrangeFilter
  2. 9 core/modules/views_ui/src/Form/Ajax/RearrangeFilter.php \Drupal\views_ui\Form\Ajax\RearrangeFilter
  3. 8.9.x core/modules/views_ui/src/Form/Ajax/RearrangeFilter.php \Drupal\views_ui\Form\Ajax\RearrangeFilter
  4. main core/modules/views_ui/src/Form/Ajax/RearrangeFilter.php \Drupal\views_ui\Form\Ajax\RearrangeFilter

Provides a rearrange form for Views filters.

@internal

Hierarchy

Expanded class hierarchy of RearrangeFilter

1 file declares its use of RearrangeFilter
RearrangeFilterTest.php in core/modules/views_ui/tests/src/Unit/Form/Ajax/RearrangeFilterTest.php

File

core/modules/views_ui/src/Form/Ajax/RearrangeFilter.php, line 14

Namespace

Drupal\views_ui\Form\Ajax
View source
class RearrangeFilter extends ViewsFormBase {
  
  /**
   * Constructs a new RearrangeFilter object.
   */
  public function __construct($type = NULL) {
    $this->setType($type);
  }
  
  /**
   * {@inheritdoc}
   */
  public function getFormKey() {
    return 'rearrange-filter';
  }
  
  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'views_ui_rearrange_filter_form';
  }
  
  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $view = $form_state->get('view');
    $display_id = $form_state->get('display_id');
    $type = 'filter';
    $types = ViewExecutable::getHandlerTypes();
    $executable = $view->getExecutable();
    if (!$executable->setDisplay($display_id)) {
      $form['markup'] = [
        '#markup' => $this->t('Invalid display id @display', [
          '@display' => $display_id,
        ]),
      ];
      return $form;
    }
    $display = $executable->displayHandlers
      ->get($display_id);
    $form['#title'] = Html::escape($display->display['display_title']) . ': ';
    $form['#title'] .= $this->t('Rearrange @type', [
      '@type' => $types[$type]['ltitle'],
    ]);
    $form['#section'] = $display_id . 'rearrange-item';
    if ($display->defaultableSections($types[$type]['plural'])) {
      $section = $types[$type]['plural'];
      $form_state->set('section', $section);
      $this->standardDisplayDropdown($form, $form_state, $section);
    }
    if (!empty($view->form_cache)) {
      $groups = $view->form_cache['groups'];
      $handlers = $view->form_cache['handlers'];
    }
    else {
      $groups = $display->getOption('filter_groups');
      $handlers = $display->getOption($types[$type]['plural']);
    }
    $count = 0;
    // Get relationship labels.
    $relationships = [];
    foreach ($display->getHandlers('relationship') as $id => $handler) {
      $relationships[$id] = $handler->adminLabel();
    }
    $group_options = [];
    
    /*
     * Filter groups is an array that contains:
     * [
     *   'operator' => 'and' || 'or',
     *   'groups' => [
     *     $group_id => 'and' || 'or',
     *   ],
     * ];
     */
    $grouping = count(array_keys($groups['groups'])) > 1;
    $form['filter_groups']['#tree'] = TRUE;
    $form['filter_groups']['operator'] = [
      '#type' => 'select',
      '#options' => [
        'AND' => $this->t('And'),
        'OR' => $this->t('Or'),
      ],
      '#default_value' => $groups['operator'],
      '#attributes' => [
        'class' => [
          'warning-on-change',
        ],
      ],
      '#title' => $this->t('Operator to use on all groups'),
      '#description' => $this->t('Either "group 0 AND group 1 AND group 2" or "group 0 OR group 1 OR group 2", etc'),
      '#access' => $grouping,
    ];
    $form['remove_groups']['#tree'] = TRUE;
    foreach ($groups['groups'] as $id => $group) {
      $form['filter_groups']['groups'][$id] = [
        '#title' => $this->t('Operator'),
        '#type' => 'select',
        '#options' => [
          'AND' => $this->t('And'),
          'OR' => $this->t('Or'),
        ],
        '#default_value' => $group,
        '#attributes' => [
          'class' => [
            'warning-on-change',
          ],
        ],
      ];
      // To prevent a notice.
      $form['remove_groups'][$id] = [];
      if ($id != 1) {
        $form['remove_groups'][$id] = [
          '#type' => 'submit',
          '#value' => $this->t('Remove group @group', [
            '@group' => $id,
          ]),
          '#id' => "views-remove-group-{$id}",
          '#attributes' => [
            'class' => [
              'views-remove-group',
            ],
          ],
          '#group' => $id,
          '#ajax' => [
            'url' => NULL,
          ],
        ];
      }
      $group_options[$id] = $id == 1 ? $this->t('Default group') : $this->t('Group @group', [
        '@group' => $id,
      ]);
      $form['#group_renders'][$id] = [];
    }
    $form['#group_options'] = $group_options;
    $form['#groups'] = $groups;
    // We don't use getHandlers() because we want items without handlers to
    // appear and show up as 'broken' so that the user can see them.
    $form['filters'] = [
      '#tree' => TRUE,
    ];
    foreach ($handlers as $id => $field) {
      // If the group does not exist, move the filters to the default group.
      if (empty($field['group']) || empty($groups['groups'][$field['group']])) {
        $field['group'] = 1;
      }
      $handler = $display->getHandler($type, $id);
      if ($grouping && $handler && !$handler->canGroup()) {
        $field['group'] = 'ungroupable';
      }
      // If not grouping and the handler is set ungroupable, move it back to
      // the default group to prevent weird errors from having it be in its
      // own group:
      if (!$grouping && $field['group'] == 'ungroupable') {
        $field['group'] = 1;
      }
      // Place this item into the proper group for rendering.
      $form['#group_renders'][$field['group']][] = $id;
      $form['filters'][$id]['weight'] = [
        '#title' => $this->t('Weight for @id', [
          '@id' => $id,
        ]),
        '#title_display' => 'invisible',
        '#type' => 'textfield',
        '#default_value' => ++$count,
        '#size' => 8,
      ];
      $form['filters'][$id]['group'] = [
        '#title' => $this->t('Group for @id', [
          '@id' => $id,
        ]),
        '#title_display' => 'invisible',
        '#type' => 'select',
        '#options' => $group_options,
        '#default_value' => $field['group'],
        '#attributes' => [
          'class' => [
            'views-region-select',
            'views-region-' . $id,
          ],
        ],
        '#access' => $field['group'] !== 'ungroupable',
      ];
      if ($handler) {
        $name = $handler->adminLabel() . ' ' . $handler->adminSummary();
        if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
          $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
        }
        $form['filters'][$id]['name'] = [
          '#markup' => $name,
        ];
      }
      else {
        $form['filters'][$id]['name'] = [
          '#markup' => $this->t('Broken field @id', [
            '@id' => $id,
          ]),
        ];
      }
      $form['filters'][$id]['removed'] = [
        '#title' => $this->t('Remove @id', [
          '@id' => $id,
        ]),
        '#title_display' => 'invisible',
        '#type' => 'checkbox',
        '#id' => 'views-removed-' . $id,
        '#attributes' => [
          'class' => [
            'views-remove-checkbox',
          ],
        ],
        '#default_value' => 0,
      ];
    }
    $view->getStandardButtons($form, $form_state, 'views_ui_rearrange_filter_form');
    $form['actions']['add_group'] = [
      '#type' => 'submit',
      '#value' => $this->t('Create new filter group'),
      '#id' => 'views-add-group',
      '#group' => 'add',
      '#attributes' => [
        'class' => [
          'views-add-group',
        ],
      ],
      '#ajax' => [
        'url' => NULL,
      ],
    ];
    return $form;
  }
  
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $types = ViewExecutable::getHandlerTypes();
    $view = $form_state->get('view');
    $display =& $view->getExecutable()->displayHandlers
      ->get($form_state->get('display_id'));
    $remember_groups = [];
    if (!empty($view->form_cache)) {
      $old_fields = $view->form_cache['handlers'];
    }
    else {
      $old_fields = $display->getOption($types['filter']['plural']);
    }
    $groups = $form_state->getValue('filter_groups');
    // Whatever button was clicked, re-calculate field information.
    $new_fields = $order = [];
    // Make an array with the weights.
    foreach ($form_state->getValue('filters') as $field => $info) {
      // Add each value that is a field with a weight to our list, but only if
      // it has had its 'removed' checkbox checked.
      if (is_array($info) && empty($info['removed'])) {
        if (isset($info['weight'])) {
          $order[$field] = $info['weight'];
        }
        if (isset($info['group'])) {
          $old_fields[$field]['group'] = $info['group'];
          $remember_groups[$info['group']][] = $field;
        }
      }
    }
    // Sort the array.
    asort($order);
    // Create a new list of fields in the new order.
    foreach (array_keys($order) as $field) {
      $new_fields[$field] = $old_fields[$field];
    }
    // If the #group property is set on the clicked button, that means we are
    // either adding or removing a group, not actually updating the filters.
    $triggering_element = $form_state->getTriggeringElement();
    if (!empty($triggering_element['#group'])) {
      if ($triggering_element['#group'] == 'add') {
        // Add a new group.
        $groups['groups'][] = 'AND';
      }
      else {
        // Renumber groups above the removed one down.
        foreach (array_keys($groups['groups']) as $group_id) {
          if ($group_id >= $triggering_element['#group']) {
            $old_group = $group_id + 1;
            if (isset($groups['groups'][$old_group])) {
              $groups['groups'][$group_id] = $groups['groups'][$old_group];
              if (isset($remember_groups[$old_group])) {
                foreach ($remember_groups[$old_group] as $id) {
                  $new_fields[$id]['group'] = $group_id;
                }
              }
            }
            else {
              // If this is the last one, just unset it.
              unset($groups['groups'][$group_id]);
            }
          }
        }
      }
      // Update our cache with values so that cancel still works the way
      // people expect.
      $view->form_cache = [
        'key' => 'rearrange-filter',
        'groups' => $groups,
        'handlers' => $new_fields,
      ];
      // Return to this form except on actual Update.
      $view->addFormToStack('rearrange-filter', $form_state->get('display_id'), 'filter');
    }
    else {
      // The actual update button was clicked. Remove the empty groups, and
      // renumber them sequentially.
      ksort($remember_groups);
      $groups['groups'] = static::arrayKeyPlus(array_values(array_intersect_key($groups['groups'], $remember_groups)));
      // Change the 'group' key on each field to match. Here, $mapping is an
      // array whose keys are the old group numbers and whose values are the new
      // (sequentially numbered) ones.
      $mapping = array_flip(static::arrayKeyPlus(array_keys($remember_groups)));
      foreach ($new_fields as &$new_field) {
        $new_field['group'] = $mapping[$new_field['group']];
      }
      // Write the changed handler values.
      $display->setOption($types['filter']['plural'], $new_fields);
      $display->setOption('filter_groups', $groups);
      if (isset($view->form_cache)) {
        unset($view->form_cache);
      }
    }
    // Store in cache.
    $view->cacheSet();
  }
  
  /**
   * Adds one to each key of an array.
   *
   * For example [0 => 'foo'] would be [1 => 'foo'].
   *
   * @param array $array
   *   The array to increment keys on.
   *
   * @return array
   *   The array with incremented keys.
   */
  public static function arrayKeyPlus($array) {
    $keys = array_keys($array);
    // Sort the keys in reverse order so incrementing them doesn't overwrite any
    // existing keys.
    rsort($keys);
    foreach ($keys as $key) {
      $array[$key + 1] = $array[$key];
      unset($array[$key]);
    }
    // Sort the keys back to ascending order.
    ksort($array);
    return $array;
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
AutowiredInstanceTrait::createInstanceAutowired public static function Instantiates a new instance of the implementing class using autowiring.
AutowiredInstanceTrait::getAutowireArguments private static function Resolves arguments for a method using autowiring.
AutowireTrait::create public static function Instantiates a new instance of the implementing class using autowiring. 135
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::config protected function Retrieves a configuration object.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user. 2
FormBase::getRequest protected function Gets the request object. Overrides HtmxRequestInfoTrait::getRequest
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route.
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
HtmxRequestInfoTrait::getHtmxCurrentUrl protected function Retrieves the URL of the requesting page from an HTMX request header.
HtmxRequestInfoTrait::getHtmxPrompt protected function Retrieves the prompt from an HTMX request header.
HtmxRequestInfoTrait::getHtmxTarget protected function Retrieves the target identifier from an HTMX request header.
HtmxRequestInfoTrait::getHtmxTrigger protected function Retrieves the trigger identifier from an HTMX request header.
HtmxRequestInfoTrait::getHtmxTriggerName protected function Retrieves the trigger name from an HTMX request header.
HtmxRequestInfoTrait::isHtmxBoosted protected function Determines if the request is boosted by HTMX.
HtmxRequestInfoTrait::isHtmxHistoryRestoration protected function Determines if if the request is for history restoration.
HtmxRequestInfoTrait::isHtmxRequest protected function Determines if the request is sent by HTMX.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 28
MessengerTrait::messenger public function Gets the messenger. 28
MessengerTrait::setMessenger public function Sets the messenger.
RearrangeFilter::arrayKeyPlus public static function Adds one to each key of an array.
RearrangeFilter::buildForm public function Form constructor. Overrides FormInterface::buildForm
RearrangeFilter::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
RearrangeFilter::getFormKey public function Returns the key that represents this form. Overrides ViewsFormInterface::getFormKey
RearrangeFilter::submitForm public function Form submission handler. Overrides ViewsFormBase::submitForm
RearrangeFilter::__construct public function Constructs a new RearrangeFilter object.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 2
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language. 1
ViewsFormBase::$id protected property The ID of the item this form is manipulating.
ViewsFormBase::$type protected property The type of item this form is manipulating.
ViewsFormBase::ajaxFormWrapper protected function Wrapper for handling AJAX forms.
ViewsFormBase::getForm public function Creates a new instance of this form. Overrides ViewsFormInterface::getForm 6
ViewsFormBase::getFormState public function Gets the form state for this form. Overrides ViewsFormInterface::getFormState 1
ViewsFormBase::setID protected function Sets the ID for this form.
ViewsFormBase::setType protected function Sets the type for this form.
ViewsFormBase::validateForm public function Form validation handler. Overrides FormBase::validateForm 3
ViewsFormHelperTrait::buildFormUrl protected function Creates the menu path for a standard AJAX form given the form state.
ViewsFormHelperTrait::formButtonWasClicked public static function The #process callback for a button.
ViewsFormHelperTrait::getConfigFactory protected function Returns the config factory service.
ViewsFormHelperTrait::standardDisplayDropdown protected function Adds an element to select either the default or the current display.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.