View source
<?php
namespace Drupal\views_exposed_groups\Plugin\views\exposed_form;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase;
use Drupal\views\Plugin\views\ViewsHandlerInterface;
class ExposedGroups extends ExposedFormPluginBase {
protected function defineOptions() {
$options = parent::defineOptions();
$options['exposed_groups'] = [
'default' => [
[
'name' => '_none',
'label' => 'Not grouped',
'weight' => 0,
'filters' => [],
],
],
];
$options['exposed_groups_format'] = [
'default' => 'vertical_tabs',
];
$options['exposed_groups_vertical_tabs_summary'] = [
'default' => 0,
];
return $options;
}
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['exposed_groups'] = [
'#tree' => FALSE,
'#attributes' => [
'id' => 'views-exposed-groups-settings-wrapper',
],
];
$form['exposed_groups']['format'] = [
'#type' => 'select',
'#title' => $this
->t('Render groups as'),
'#description' => $this
->t('Choose the way that the grouped filters will be displayed.'),
'#parents' => [
'exposed_form_options',
'exposed_groups_format',
],
'#options' => [
'vertical_tabs' => $this
->t('Vertical tabs'),
'fieldsets_collapsed' => $this
->t('Fieldsets (collapsed)'),
'fieldsets' => $this
->t('Fieldsets (expanded)'),
],
'#default_value' => $this->options['exposed_groups_format'],
];
$form['exposed_groups']['vertical_tabs_summary'] = [
'#type' => 'select',
'#title' => $this
->t('Use vertical tabs summaries'),
'#description' => $this
->t('Enable this option to display a summary of filters on the vertical tab link.'),
'#parents' => [
'exposed_form_options',
'exposed_groups_vertical_tabs_summary',
],
'#options' => [
0 => $this
->t('Do not add summaries'),
1 => $this
->t('Add summaries'),
],
'#default_value' => (int) $this->options['exposed_groups_vertical_tabs_summary'],
'#states' => [
'visible' => [
[
':input[name="exposed_form_options[exposed_groups_format]"]' => [
'value' => 'vertical_tabs',
],
],
],
],
];
$headers = [
[
'data' => $this
->t('Filter'),
],
[
'data' => $this
->t('Group'),
],
[
'data' => $this
->t('Weight'),
],
];
$form['exposed_groups']['groups'] = [
'#type' => 'table',
'#header' => $headers,
'#empty' => $this
->t('This view does not have any filters.'),
'#attributes' => [
'id' => 'views-exposed-groups-table',
],
'#tabledrag' => [],
'#parents' => [
'exposed_form_options',
'groups',
],
];
$allFilters = $this->displayHandler
->getHandlers('filter');
$filters = array_filter($allFilters, function (ViewsHandlerInterface $filter) {
return $filter
->isExposed();
});
$weight_delta = round(count($filters) / 2);
$groups = $this->options['exposed_groups'];
foreach ($groups as $group) {
$group_name_class_replace = Xss::filter(str_replace('_', '-', $group['name']));
$form['exposed_groups']['groups']['#tabledrag'][] = [
'action' => 'match',
'relationship' => 'sibling',
'group' => 'filter-group-select',
'subgroup' => 'filter-group-' . $group_name_class_replace,
'hidden' => TRUE,
'limit' => FALSE,
];
$form['exposed_groups']['groups']['#tabledrag'][] = [
'action' => 'order',
'relationship' => 'sibling',
'group' => 'filter-weight',
'subgroup' => 'filter-weight-' . $group_name_class_replace,
];
$form['exposed_groups']['groups'][$group['name']] = [
'#attributes' => [
'colspan' => 4,
'class' => [
'tabledrag-root',
],
'no_striping' => TRUE,
],
];
if ($group['name'] === '_none') {
$form['exposed_groups']['groups']['_none']['title'] = [
'#plain_text' => $this
->t('Not grouped'),
];
$form['exposed_groups']['groups'][$group['name']]['#weight'] = 999;
}
else {
$form['exposed_groups']['groups'][$group['name']]['title'] = [
'#prefix' => Xss::filter($group['label']),
'#type' => 'button',
'#value' => $this
->t('Remove group'),
];
}
}
$default_group = array_reduce($groups, function (&$result, $group) {
if ($result !== NULL && $group['name'] === '_none') {
return $group;
}
return $result;
}, NULL);
$group_options = [];
foreach ($groups as $index => $group) {
$group_options[$group['name']] = $group['label'] !== NULL ? Xss::filter($group['label']) : $this
->t('Not grouped');
}
foreach ($filters as $filter_name => $filter) {
$id = $filter->options['expose']['identifier'];
$label = $filter->options['expose']['label'];
$group = $default_group;
foreach ($groups as $gindex => $g) {
foreach ($g['filters'] as $f) {
if ($f['id'] === $id) {
$group = $g;
}
}
}
$group_name = $group['name'];
$group_name_class = Xss::filter(str_replace('_', '-', $group_name));
$grouped_filter = NULL;
if ($group['filters'] !== NULL) {
$grouped_filter = array_reduce($group['filters'], function (&$result, array $f) use ($id) {
if ($result !== NULL && $f['id'] === $id) {
return $f;
}
return $result;
}, NULL);
}
$grouped_filter = $grouped_filter !== NULL ? $grouped_filter : [
'id' => $id,
'weight' => 0,
];
$weight = $grouped_filter['weight'];
$form['exposed_groups']['groups']['filter-' . $id] = [
'#attributes' => [
'class' => [
'draggable',
'tabledrag-leaf',
],
],
'#tree' => TRUE,
'#parents' => [
'exposed_form_options',
'filters',
$id,
],
'filter' => [
'id' => [
'#type' => 'value',
'#value' => $id,
],
'title' => [
'#plain_text' => $label,
],
],
'group' => [
'#type' => 'select',
'#title' => $this
->t('Group'),
'#title_display' => 'invisible',
'#attributes' => [
'class' => [
'filter-group-select',
'filter-group-' . $group_name_class,
],
],
'#options' => $group_options,
],
'weight' => [
'#type' => 'weight',
'#title' => $this
->t('Weight for @label', [
'@label' => $label,
]),
'#title_display' => 'invisible',
'#delta' => $weight_delta,
'#filter_field' => $filter,
'#default_value' => $weight,
'#attributes' => [
'class' => [
'filter-weight',
'filter-weight-' . $group_name_class,
],
],
],
];
}
$groups_value = '';
foreach ($this->options['exposed_groups'] as $group_weight => $group) {
$groups_value .= ($group['name'] !== '_none' ? $group['label'] : '_none') . "\n";
}
$groups_value = trim($groups_value);
$form['exposed_groups']['manage_groups'] = [
'#tree' => TRUE,
'groups' => [
'#type' => 'textarea',
'#title' => $this
->t('Groups'),
'#rows' => 5,
'#columns' => 40,
'#default_value' => Html::escape($groups_value),
'#element_validate' => [
[
$this,
'updateGroupsValidate',
],
],
],
];
}
public function updateGroupsValidate(array &$element, FormStateInterface $formState) {
$groups_value = $formState
->getValue([
'manage_groups',
'groups',
]);
$groups = explode("\n", $groups_value);
$groups = array_map('trim', $groups);
if (!in_array('_none', $groups)) {
$formState
->setError($element, $this
->t('The "_none" group is required.'));
}
foreach ($groups as $group_label) {
$groups_with_label = array_filter($groups, function ($group) use ($group_label) {
return $group === $group_label;
});
if (count($groups_with_label) > 1) {
$formState
->setError($element, $this
->t('Duplicate group "@name" found.', [
'@name' => $group_label,
]));
}
}
}
public function submitOptionsForm(&$form, FormStateInterface $form_state) {
$parents = [
'manage_groups',
'groups',
];
$groups_value = $form_state
->getValue($parents);
$new_groups = explode("\n", $groups_value);
$new_groups = array_map(function ($group_weight, $group_label) {
$label = trim($group_label);
return [
'name' => strtolower(preg_replace('/[^a-zA-Z0-9_]/', '_', $label)),
'label' => $label,
'weight' => $group_weight,
'filters' => [],
];
}, array_keys($new_groups), $new_groups);
$group_names = array_reduce($new_groups, function (&$result, $group) {
$result[] = $group['name'];
return $result;
}, []);
$filters = $form_state
->getValue([
'exposed_form_options',
'filters',
]);
$none_index = array_search('_none', $group_names);
foreach ($filters as $id => $filter) {
$index = array_search($filter['group'], $group_names);
if ($index === FALSE) {
$index = $none_index;
}
$new_groups[$index]['filters'][] = [
'id' => $id,
'weight' => $filter['weight'],
];
}
$this->options['exposed_groups'] = $new_groups;
$form_state
->setValue([
'exposed_form_options',
'exposed_groups',
], $new_groups);
parent::submitOptionsForm($form, $form_state);
}
public function exposedFormAlter(&$form, FormStateInterface $form_state) {
parent::exposedFormAlter($form, $form_state);
if (!isset($form['#attributes']['class'])) {
$form['#attributes']['class'] = [];
}
$form['#attributes']['class'][] = 'views-exposed-form--views-exposed-group';
if ($this->options['submit_button']) {
$form['submit']['#value'] = $this->options['submit_button'];
}
if ($this->options['exposed_groups_format'] === 'vertical_tabs') {
$form['filters'] = [
'#type' => 'vertical_tabs',
'#weight' => -10,
'#attached' => [
'library' => [
'views_exposed_groups/views_exposed_groups',
],
'drupalSettings' => [
'viewsExposedGroups' => [
'id' => $form['#id'],
'options' => $this->options,
],
],
],
];
}
else {
$form['filters'] = [
'#weight' => -10,
];
}
$groups = $this->options['exposed_groups'];
foreach ($groups as $index => $group) {
if ($group['name'] !== '_none') {
$form['filters'][$group['name']] = [
'#type' => 'fieldset',
'#title' => Xss::filter($group['label']),
'#collapsible' => TRUE,
'#collapsed' => $this->options['exposed_groups_format'] === 'fieldsets_collapsed',
'#weight' => $group['weight'],
];
}
else {
$form['filters'][$group['name']] = [
'#weight' => $group['weight'],
];
}
$element =& $form['filters'][$group['name']];
foreach ($group['filters'] as $id => $filter_info) {
if (isset($form[$id]) && is_array($form[$id])) {
$element[$id] = $form[$id] + [
'#weight' => $filter_info['#weight'],
];
if (isset($form[$id . '_op']) && is_array($form[$id . '_op'])) {
unset($form[$id . '_op']);
}
unset($form[$id]);
}
}
}
if (!empty($this->options['reset_button'])) {
$form['reset'] = [
'#value' => $this->options['reset_button_label'],
'#type' => 'submit',
];
}
}
}