class BetterExposedFilters in Better Exposed Filters 8.4
Same name and namespace in other branches
- 8.5 src/Plugin/views/exposed_form/BetterExposedFilters.php \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters
- 8.3 src/Plugin/views/exposed_form/BetterExposedFilters.php \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters
Exposed form plugin that provides a basic exposed form.
Plugin annotation
@ViewsExposedForm(
id = "bef",
title = @Translation("Better Exposed Filters"),
help = @Translation("Provides additional options for exposed form elements.")
)
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\views\Plugin\views\PluginBase implements DependentPluginInterface, ContainerFactoryPluginInterface, TrustedCallbackInterface, ViewsPluginInterface
- class \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase implements CacheableDependencyInterface, ExposedFormPluginInterface
- class \Drupal\views\Plugin\views\exposed_form\InputRequired
- class \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters
- class \Drupal\views\Plugin\views\exposed_form\InputRequired
- class \Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase implements CacheableDependencyInterface, ExposedFormPluginInterface
- class \Drupal\views\Plugin\views\PluginBase implements DependentPluginInterface, ContainerFactoryPluginInterface, TrustedCallbackInterface, ViewsPluginInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of BetterExposedFilters
File
- src/
Plugin/ views/ exposed_form/ BetterExposedFilters.php, line 26
Namespace
Drupal\better_exposed_filters\Plugin\views\exposed_formView source
class BetterExposedFilters extends InputRequired {
/**
* BEF filters widget plugin manager.
*
* @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager
*/
public $filterWidgetManager;
/**
* BEF pager widget plugin manager.
*
* @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager
*/
public $pagerWidgetManager;
/**
* BEF sort widget plugin manager.
*
* @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager
*/
public $sortWidgetManager;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* BetterExposedFilters constructor.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager $filter_widget_manager
* The better exposed filter widget manager for filter widgets.
* @param \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager $pager_widget_manager
* The better exposed filter widget manager for pager widgets.
* @param \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetManager $sort_widget_manager
* The better exposed filter widget manager for sort widgets.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* Manage drupal modules.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, BetterExposedFiltersWidgetManager $filter_widget_manager, BetterExposedFiltersWidgetManager $pager_widget_manager, BetterExposedFiltersWidgetManager $sort_widget_manager, ModuleHandlerInterface $module_handler) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->filterWidgetManager = $filter_widget_manager;
$this->pagerWidgetManager = $pager_widget_manager;
$this->sortWidgetManager = $sort_widget_manager;
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('plugin.manager.better_exposed_filters_filter_widget'), $container
->get('plugin.manager.better_exposed_filters_pager_widget'), $container
->get('plugin.manager.better_exposed_filters_sort_widget'), $container
->get('module_handler'));
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
// General, sort, pagers, and filter.
$bef_options = [
'general' => [
'autosubmit' => FALSE,
'autosubmit_exclude_textfield' => FALSE,
'autosubmit_textfield_delay' => 500,
'autosubmit_hide' => FALSE,
'input_required' => FALSE,
'allow_secondary' => FALSE,
'secondary_label' => $this
->t('Advanced options'),
'secondary_open' => FALSE,
],
'sort' => [
'plugin_id' => 'default',
],
];
// Initialize options if any sort is exposed.
// Iterate over each sort and determine if any sorts are exposed.
$is_sort_exposed = FALSE;
/** @var \Drupal\views\Plugin\views\HandlerBase $sort */
foreach ($this->view->display_handler
->getHandlers('sort') as $sort) {
if ($sort
->isExposed()) {
$is_sort_exposed = TRUE;
break;
}
}
if ($is_sort_exposed) {
$bef_options['sort']['plugin_id'] = 'default';
}
// Initialize options if the pager is exposed.
$pager = $this->view
->getPager();
if ($pager && $pager
->usesExposed()) {
$bef_options['pager']['plugin_id'] = 'default';
}
// Go through each exposed filter and set default format.
/** @var \Drupal\views\Plugin\views\HandlerBase $filter */
foreach ($this->view->display_handler
->getHandlers('filter') as $filter_id => $filter) {
if (!$filter
->isExposed()) {
continue;
}
$bef_options['filter'][$filter_id]['plugin_id'] = 'default';
}
// Iterate over bef options and convert them to be compatible with views
// default options.
$options += $this
->createOptionDefaults([
'bef' => $bef_options,
]);
return $options;
}
/**
* Creates a list of view handler default options.
*
* Views handlers expect default options in a specific format.
*
* @param array $options
* An array of plugin defaults.
*
* @return array
* An array of plugin options.
*
* @see \Drupal\views\Plugin\views\PluginBase::setOptionDefaults
*/
protected function createOptionDefaults(array $options) {
$result = [];
foreach ($options as $key => $option) {
if (is_array($option)) {
$result[$key]['contains'] = $this
->createOptionDefaults($option);
}
else {
$result[$key]['default'] = $option;
}
}
return $result;
}
/**
* Build the views options form and adds custom options for BEF.
*
* @inheritDoc
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
// Ensure that the form values are stored in their original location, and
// not dependent on their position in the form tree. We are moving around
// a few elements to make the UI more user friendly.
$original_form = [];
parent::buildOptionsForm($original_form, $form_state);
foreach (Element::children($original_form) as $element) {
$original_form[$element]['#parents'] = [
'exposed_form_options',
$element,
];
}
// Save shorthand for BEF options.
$bef_options = $this->options['bef'];
// User raw user input for AJAX callbacks.
$user_input = $form_state
->getUserInput();
$bef_input = $user_input['exposed_form_options']['bef'] ?? NULL;
/*
* General BEF settings
*/
// Reorder some existing form elements.
$form['bef']['general']['submit_button'] = $original_form['submit_button'];
$form['bef']['general']['reset_button'] = $original_form['reset_button'];
$form['bef']['general']['reset_button_label'] = $original_form['reset_button_label'];
// Add the 'auto-submit' functionality.
$form['bef']['general']['autosubmit'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Enable auto-submit'),
'#description' => $this
->t('Automatically submits the form when an element has changed.'),
'#default_value' => $bef_options['general']['autosubmit'],
];
$form['bef']['general']['autosubmit_exclude_textfield'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Exclude Textfield'),
'#description' => $this
->t('Exclude textfields from auto-submit. User will have to press enter key, or click submit button.'),
'#default_value' => $bef_options['general']['autosubmit_exclude_textfield'],
'#states' => [
'visible' => [
':input[name="exposed_form_options[bef][general][autosubmit]"]' => [
'checked' => TRUE,
],
],
],
];
$form['bef']['general']['autosubmit_textfield_delay'] = [
'#type' => 'number',
'#title' => $this
->t('Delay for textfield autosubmit'),
'#description' => $this
->t('Configure a delay in ms before triggering autosubmit on textfields.'),
'#default_value' => $bef_options['general']['autosubmit_textfield_delay'],
'#min' => 0,
'#states' => [
'visible' => [
':input[name="exposed_form_options[bef][general][autosubmit]"]' => [
'checked' => TRUE,
],
':input[name="exposed_form_options[bef][general][autosubmit_exclude_textfield]"]' => [
'checked' => FALSE,
],
],
],
];
$form['bef']['general']['autosubmit_hide'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Hide submit button'),
'#description' => $this
->t('Hides submit button if auto-submit and javascript are enabled.'),
'#default_value' => $bef_options['general']['autosubmit_hide'],
'#states' => [
'visible' => [
':input[name="exposed_form_options[bef][general][autosubmit]"]' => [
'checked' => TRUE,
],
],
],
];
// Insert a checkbox to make the input required optional just before the
// input required text field. Only show the text field if the input required
// option is selected.
$form['bef']['general']['input_required'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Input required'),
'#description' => $this
->t('Only display results after the user has selected a filter option.'),
'#default_value' => $bef_options['general']['input_required'],
];
$original_form['text_input_required'] += [
'#states' => [
'visible' => [
'input[name="exposed_form_options[bef][general][input_required]"]' => [
'checked' => TRUE,
],
],
],
];
$form['bef']['general']['text_input_required'] = $original_form['text_input_required'];
/*
* Allow exposed form items to be displayed as secondary options.
*/
$form['bef']['general']['allow_secondary'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Enable secondary exposed form options'),
'#default_value' => $bef_options['general']['allow_secondary'],
'#description' => $this
->t('Allows you to specify some exposed form elements as being secondary options and places those elements in a collapsible "details" element. Use this option to place some exposed filters in an "Advanced Search" area of the form, for example.'),
];
$form['bef']['general']['secondary_label'] = [
'#type' => 'textfield',
'#default_value' => $bef_options['general']['secondary_label'],
'#title' => $this
->t('Secondary options label'),
'#description' => $this
->t('The name of the details element to hold secondary options. This cannot be left blank or there will be no way to show/hide these options.'),
'#states' => [
'required' => [
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => [
'checked' => TRUE,
],
],
'visible' => [
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => [
'checked' => TRUE,
],
],
],
];
$form['bef']['general']['secondary_open'] = [
'#type' => 'checkbox',
'#default_value' => $bef_options['general']['secondary_open'],
'#title' => $this
->t('Secondary option open by default'),
'#description' => $this
->t('Indicates whether the details element should be open by default.'),
'#states' => [
'visible' => [
':input[name="exposed_form_options[bef][general][allow_secondary]"]' => [
'checked' => TRUE,
],
],
],
];
/*
* Add options for exposed sorts.
*/
// Add intro explaining BEF sorts.
$documentation_uri = Url::fromUri('http://drupal.org/node/1701012')
->toString();
$form['bef']['sort']['bef_intro'] = [
'#markup' => '<h3>' . $this
->t('Exposed Sort Settings') . '</h3><p>' . $this
->t('This section lets you select additional options for exposed sorts. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', [
':link' => $documentation_uri,
]) . '</p>',
];
// Iterate over each sort and determine if any sorts are exposed.
$is_sort_exposed = FALSE;
/** @var \Drupal\views\Plugin\views\HandlerBase $sort */
foreach ($this->view->display_handler
->getHandlers('sort') as $sort) {
if ($sort
->isExposed()) {
$is_sort_exposed = TRUE;
break;
}
}
$form['bef']['sort']['empty'] = [
'#type' => 'item',
'#description' => $this
->t('No sort elements have been exposed yet.'),
'#access' => !$is_sort_exposed,
];
if ($is_sort_exposed) {
$options = [];
foreach ($this->sortWidgetManager
->getDefinitions() as $plugin_id => $definition) {
if ($definition['class']::isApplicable()) {
$options[$plugin_id] = $definition['label'];
}
}
$form['bef']['sort']['configuration'] = [
'#prefix' => "<div id='bef-sort-configuration'>",
'#suffix' => "</div>",
'#type' => 'container',
];
// Get selected plugin_id on AJAX callback directly from the form state.
$selected_plugin_id = $bef_input['sort']['configuration']['plugin_id'] ?? $bef_options['sort']['plugin_id'];
$form['bef']['sort']['configuration']['plugin_id'] = [
'#type' => 'select',
'#title' => $this
->t('Display exposed sort options as'),
'#default_value' => $selected_plugin_id,
'#options' => $options,
'#description' => $this
->t('Select a format for the exposed sort options.'),
'#ajax' => [
'event' => 'change',
'effect' => 'fade',
'progress' => 'throbber',
// Since views options forms are complex, they're built by
// Drupal in a different way. To bypass this problem we need to
// provide the full path to the Ajax callback.
'callback' => __CLASS__ . '::ajaxCallback',
'wrapper' => 'bef-sort-configuration',
],
];
// Move some existing form elements.
$form['bef']['sort']['configuration']['exposed_sorts_label'] = $original_form['exposed_sorts_label'];
$form['bef']['sort']['configuration']['expose_sort_order'] = $original_form['expose_sort_order'];
$form['bef']['sort']['configuration']['sort_asc_label'] = $original_form['sort_asc_label'];
$form['bef']['sort']['configuration']['sort_desc_label'] = $original_form['sort_desc_label'];
if ($selected_plugin_id) {
$plugin_configuration = $bef_options['sort'] ?? [];
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->sortWidgetManager
->createInstance($selected_plugin_id, $plugin_configuration);
$plugin
->setView($this->view);
$subform =& $form['bef']['sort']['configuration'];
$subform_state = SubformState::createForSubform($subform, $form, $form_state);
$subform += $plugin
->buildConfigurationForm($subform, $subform_state);
}
}
/*
* Add options for exposed pager.
*/
$documentation_uri = Url::fromUri('http://drupal.org/node/1701012')
->toString();
$form['bef']['pager']['bef_intro'] = [
'#markup' => '<h3>' . $this
->t('Exposed Pager Settings') . '</h3><p>' . $this
->t('This section lets you select additional options for exposed pagers. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', [
':link' => $documentation_uri,
]) . '</p>',
];
$pager = $this->view
->getPager();
$is_pager_exposed = $pager && $pager
->usesExposed();
$form['bef']['pager']['empty'] = [
'#type' => 'item',
'#description' => $this
->t('No pager elements have been exposed yet.'),
'#access' => !$is_pager_exposed,
];
if ($is_pager_exposed) {
$options = [];
foreach ($this->pagerWidgetManager
->getDefinitions() as $plugin_id => $definition) {
if ($definition['class']::isApplicable()) {
$options[$plugin_id] = $definition['label'];
}
}
$form['bef']['pager']['configuration'] = [
'#prefix' => "<div id='bef-pager-configuration'>",
'#suffix' => "</div>",
'#type' => 'container',
];
// Get selected plugin_id on AJAX callback directly from the form state.
$selected_plugin_id = $bef_input['pager']['configuration']['plugin_id'] ?? $bef_options['pager']['plugin_id'];
$form['bef']['pager']['configuration']['plugin_id'] = [
'#type' => 'select',
'#title' => $this
->t('Display exposed pager options as'),
'#default_value' => $selected_plugin_id,
'#options' => $options,
'#description' => $this
->t('Select a format for the exposed pager options.'),
'#ajax' => [
'event' => 'change',
'effect' => 'fade',
'progress' => 'throbber',
// Since views options forms are complex, they're built by
// Drupal in a different way. To bypass this problem we need to
// provide the full path to the Ajax callback.
'callback' => __CLASS__ . '::ajaxCallback',
'wrapper' => 'bef-pager-configuration',
],
];
if ($selected_plugin_id) {
$plugin_configuration = $bef_options['pager'] ?? [];
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->pagerWidgetManager
->createInstance($selected_plugin_id, $plugin_configuration);
$plugin
->setView($this->view);
$subform =& $form['bef']['pager']['configuration'];
$subform_state = SubformState::createForSubform($subform, $form, $form_state);
$subform += $plugin
->buildConfigurationForm($subform, $subform_state);
}
}
/*
* Add options for exposed filters.
*/
$documentation_uri = Url::fromUri('http://drupal.org/node/1701012')
->toString();
$form['bef']['filter']['bef_intro'] = [
'#markup' => '<h3>' . $this
->t('Exposed Filter Settings') . '</h3><p>' . $this
->t('This section lets you select additional options for exposed filters. Some options are only available in certain situations. If you do not see the options you expect, please see the <a href=":link">BEF settings documentation page</a> for more details.', [
':link' => $documentation_uri,
]) . '</p>',
];
// Iterate over each filter and add BEF filter options.
/** @var \Drupal\views\Plugin\views\HandlerBase $filter */
foreach ($this->view->display_handler
->getHandlers('filter') as $filter_id => $filter) {
if (!$filter
->isExposed()) {
continue;
}
$options = [];
foreach ($this->filterWidgetManager
->getDefinitions() as $plugin_id => $definition) {
if ($definition['class']::isApplicable($filter, $this->displayHandler->handlers['filter'][$filter_id]->options)) {
$options[$plugin_id] = $definition['label'];
}
}
// Alter the list of available widgets for this filter.
$this->moduleHandler
->alter('better_exposed_filters_display_options', $options, $filter);
// Get a descriptive label for the filter.
$label = $this
->t('Exposed filter @filter', [
'@filter' => $filter->options['expose']['identifier'],
]);
if (!empty($filter->options['expose']['label'])) {
$label = $this
->t('Exposed filter "@filter" with label "@label"', [
'@filter' => $filter->options['expose']['identifier'],
'@label' => $filter->options['expose']['label'],
]);
}
$form['bef']['filter'][$filter_id] = [
'#type' => 'details',
'#title' => $label,
'#collapsed' => FALSE,
'#collapsible' => TRUE,
];
$form['bef']['filter'][$filter_id]['configuration'] = [
'#prefix' => "<div id='bef-filter-{$filter_id}-configuration'>",
'#suffix' => "</div>",
'#type' => 'container',
];
// Get selected plugin_id on AJAX callback directly from the form state.
$selected_plugin_id = $bef_input['filter'][$filter_id]['configuration']['plugin_id'] ?? $bef_options['filter'][$filter_id]['plugin_id'];
$form['bef']['filter'][$filter_id]['configuration']['plugin_id'] = [
'#type' => 'select',
'#title' => $this
->t('Exposed filter widget:'),
'#default_value' => $selected_plugin_id,
'#options' => $options,
'#ajax' => [
'event' => 'change',
'effect' => 'fade',
'progress' => 'throbber',
// Since views options forms are complex, they're built by
// Drupal in a different way. To bypass this problem we need to
// provide the full path to the Ajax callback.
'callback' => __CLASS__ . '::ajaxCallback',
'wrapper' => 'bef-filter-' . $filter_id . '-configuration',
],
];
if ($selected_plugin_id) {
$plugin_configuration = $bef_options['filter'][$filter_id] ?? [];
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->filterWidgetManager
->createInstance($selected_plugin_id, $plugin_configuration);
$plugin
->setView($this->view);
$plugin
->setViewsHandler($filter);
$subform =& $form['bef']['filter'][$filter_id]['configuration'];
$subform_state = SubformState::createForSubform($subform, $form, $form_state);
$subform += $plugin
->buildConfigurationForm($subform, $subform_state);
}
}
}
/**
* The form ajax callback.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return array
* The form element to return.
*/
public static function ajaxCallback(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
return NestedArray::getValue($form, array_slice($triggering_element['#array_parents'], 0, -1));
}
/**
* {@inheritdoc}
*/
public function validateOptionsForm(&$form, FormStateInterface $form_state) {
// Drupal only passes in a part of the views form, but we need the complete
// form array for plugin subforms to work.
$parent_form = $form_state
->getCompleteForm();
// Save a shorthand to the BEF form.
$bef_form =& $form['bef'];
// Save a shorthand to the BEF options.
$bef_form_options = $form_state
->getValue([
'exposed_form_options',
'bef',
]);
parent::validateOptionsForm($form, $form_state);
// Skip plugin validation if we are switching between bef plugins.
$triggering_element = $form_state
->getTriggeringElement();
if ($triggering_element['#type'] !== 'submit') {
return;
}
// Shorthand for all filter handlers in this view.
/** @var \Drupal\views\Plugin\views\HandlerBase[] $filters */
$filters = $this->view->display_handler->handlers['filter'];
// Iterate over all filter, sort and pager plugins.
foreach ($bef_form_options as $type => $config) {
// Validate exposed filter configuration.
if ($type === 'filter') {
foreach ($config as $filter_id => $filter_options) {
$plugin_id = $filter_options['configuration']['plugin_id'] ?? NULL;
if (!$plugin_id) {
continue;
}
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->filterWidgetManager
->createInstance($plugin_id);
$subform =& $bef_form[$type][$filter_id]['configuration'];
$subform_state = SubformState::createForSubform($subform, $parent_form, $form_state);
$plugin
->setView($this->view);
$plugin
->setViewsHandler($filters[$filter_id]);
$plugin
->validateConfigurationForm($subform, $subform_state);
}
}
elseif (in_array($type, [
'pager',
'sort',
])) {
$plugin_id = $config['configuration']['plugin_id'] ?? NULL;
if (!$plugin_id) {
continue;
}
// Use the correct widget manager.
if ($type === 'pager') {
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->pagerWidgetManager
->createInstance($plugin_id);
}
else {
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->sortWidgetManager
->createInstance($plugin_id);
}
$subform =& $bef_form[$type]['configuration'];
$subform_state = SubformState::createForSubform($subform, $parent_form, $form_state);
$plugin
->setView($this->view);
$plugin
->validateConfigurationForm($subform, $subform_state);
}
}
}
/**
* {@inheritdoc}
*/
public function submitOptionsForm(&$form, FormStateInterface $form_state) {
// Drupal only passes in a part of the views form, but we need the complete
// form array for plugin subforms to work.
$parent_form = $form_state
->getCompleteForm();
// Save a shorthand to the BEF form.
$bef_form =& $form['bef'];
// Reorder options based on config - some keys may have shifted because of
// form alterations (@see \Drupal\better_exposed_filters\Plugin\views\exposed_form\BetterExposedFilters::buildOptionsForm).
$options =& $form_state
->getValue('exposed_form_options');
$options = array_replace_recursive($this->options, $options);
// Save a shorthand to the BEF options.
$bef_options =& $options['bef'];
// Shorthand for all filter handlers in this view.
/** @var \Drupal\views\Plugin\views\HandlerBase[] $filters */
$filters = $this->view->display_handler->handlers['filter'];
parent::submitOptionsForm($form, $form_state);
// Iterate over all filter, sort and pager plugins.
foreach ($bef_options as $type => $config) {
// Save exposed filter configuration.
if ($type === 'filter') {
foreach ($config as $filter_id => $filter_options) {
$plugin_id = $filter_options['configuration']['plugin_id'] ?? NULL;
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
if (!$plugin_id) {
unset($bef_options['filter'][$filter_id]);
continue;
}
$plugin = $this->filterWidgetManager
->createInstance($plugin_id);
$subform =& $bef_form[$type][$filter_id]['configuration'];
$subform_state = SubformState::createForSubform($subform, $parent_form, $form_state);
$plugin
->setView($this->view);
$plugin
->setViewsHandler($filters[$filter_id]);
$plugin
->submitConfigurationForm($subform, $subform_state);
$plugin_configuration = $plugin
->getConfiguration();
$bef_options[$type][$filter_id] = $plugin_configuration;
}
}
elseif (in_array($type, [
'pager',
'sort',
])) {
$plugin_id = $config['configuration']['plugin_id'] ?? NULL;
if (!$plugin_id) {
unset($bef_options[$type]);
continue;
}
// Use the correct widget manager.
if ($type === 'pager') {
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->pagerWidgetManager
->createInstance($plugin_id);
}
else {
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->sortWidgetManager
->createInstance($plugin_id);
}
$subform =& $bef_form[$type]['configuration'];
$subform_state = SubformState::createForSubform($subform, $parent_form, $form_state);
$plugin
->setView($this->view);
$plugin
->submitConfigurationForm($subform, $subform_state);
$plugin_configuration = $plugin
->getConfiguration();
$bef_options[$type] = $plugin_configuration;
}
}
}
/**
* {@inheritdoc}
*/
public function exposedFormAlter(&$form, FormStateInterface $form_state) {
parent::exposedFormAlter($form, $form_state);
// Mark form as Better Exposed Filter form for easier alterations.
$form['#context']['bef'] = TRUE;
// These styles are used on all exposed forms.
$form['#attached']['library'][] = 'better_exposed_filters/general';
// Add the bef-exposed-form class at the form level so we can limit some
// styling changes to just BEF forms.
$form['#attributes']['class'][] = 'bef-exposed-form';
// Grab BEF options and allow modules/theme to modify them before
// processing.
$bef_options = $this->options['bef'];
$this->moduleHandler
->alter('better_exposed_filters_options', $bef_options, $this->view, $this->displayHandler);
// Apply auto-submit values.
if (!empty($bef_options['general']['autosubmit'])) {
$form = array_merge_recursive($form, [
'#attributes' => [
'data-bef-auto-submit-full-form' => '',
'data-bef-auto-submit' => '',
'data-bef-auto-submit-delay' => $bef_options['general']['autosubmit_textfield_delay'],
],
]);
$form['actions']['submit']['#attributes']['data-bef-auto-submit-click'] = '';
$form['#attached']['library'][] = 'better_exposed_filters/auto_submit';
if (!empty($bef_options['general']['autosubmit_exclude_textfield'])) {
$supported_types = [
'entity_autocomplete',
'textfield',
];
foreach ($form as &$element) {
$element_type = $element['#type'] ?? NULL;
if (in_array($element_type, $supported_types)) {
$element['#attributes']['data-bef-auto-submit-exclude'] = '';
}
}
}
if (!empty($bef_options['general']['autosubmit_hide'])) {
$form['actions']['submit']['#attributes']['class'][] = 'js-hide';
}
}
// Some elements may be placed in a secondary details element (eg: "Advanced
// search options"). Place this after the exposed filters and before the
// rest of the items in the exposed form.
$allow_secondary = $bef_options['general']['allow_secondary'];
if ($allow_secondary) {
$form['secondary'] = [
'#attributes' => [
'class' => [
'bef--secondary',
],
],
'#type' => 'details',
'#title' => $bef_options['general']['secondary_label'],
'#open' => $bef_options['general']['secondary_open'],
// Disable until fields are added to this fieldset.
'#access' => FALSE,
];
}
/*
* Handle exposed sort elements.
*/
if (isset($bef_options['sort']['plugin_id']) && !empty($form['sort_by'])) {
$plugin_id = $bef_options['sort']['plugin_id'];
$plugin_configuration = $bef_options['sort'];
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->sortWidgetManager
->createInstance($plugin_id, $plugin_configuration);
$plugin
->setView($this->view);
$plugin
->exposedFormAlter($form, $form_state);
}
/*
* Handle exposed pager elements.
*/
$pager = $this->view
->getPager();
$is_pager_exposed = $pager && $pager
->usesExposed();
if ($is_pager_exposed && !empty($bef_options['pager']['plugin_id'])) {
$plugin_id = $bef_options['pager']['plugin_id'];
$plugin_configuration = $bef_options['pager'];
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->pagerWidgetManager
->createInstance($plugin_id, $plugin_configuration);
$plugin
->setView($this->view);
$plugin
->exposedFormAlter($form, $form_state);
}
/*
* Handle exposed filters.
*/
// Shorthand for all filter handlers in this view.
/** @var \Drupal\views\Plugin\views\HandlerBase[] $filters */
$filters = $this->view->display_handler->handlers['filter'];
// Iterate over all exposed filters.
if (!empty($bef_options['filter'])) {
foreach ($bef_options['filter'] as $filter_id => $filter_options) {
// Sanity check: Ensure this filter is an exposed filter.
if (empty($filters[$filter_id]) || !$filters[$filter_id]
->isExposed()) {
continue;
}
$plugin_id = $filter_options['plugin_id'];
if ($plugin_id) {
/** @var \Drupal\better_exposed_filters\Plugin\BetterExposedFiltersWidgetInterface $plugin */
$plugin = $this->filterWidgetManager
->createInstance($plugin_id, $filter_options);
$plugin
->setView($this->view);
$plugin
->setViewsHandler($filters[$filter_id]);
$plugin
->exposedFormAlter($form, $form_state);
}
}
}
// If our form has no visible filters, hide the submit button.
$has_visible_filters = !empty(Element::getVisibleChildren($form)) ?: FALSE;
$form['actions']['submit']['#access'] = $has_visible_filters;
// Never enable a reset button that has already been disabled.
if (!isset($form['actions']['reset']['#access']) || $form['actions']['reset']['#access'] === TRUE) {
$form['actions']['reset']['#access'] = $has_visible_filters;
}
// Ensure default process/pre_render callbacks are included when a BEF
// widget has added their own.
foreach (Element::children($form) as $key) {
$element =& $form[$key];
$this
->addDefaultElementInfo($element);
}
}
/**
* {@inheritdoc}
*/
protected function exposedFilterApplied() {
// If the input required option is set, check to see if a filter option has
// been set.
if (!empty($this->options['bef']['general']['input_required'])) {
return parent::exposedFilterApplied();
}
else {
return TRUE;
}
}
/**
* Inserts a new form element before another element identified by $key.
*
* This can be useful when reordering existing form elements without weights.
*
* @param array $form
* The form array to insert the element into.
* @param string $key
* The key of the form element you want to prepend the new form element.
* @param array $element
* The form element to insert.
*
* @return array
* The form array containing the newly inserted element.
*/
protected function prependFormElement(array $form, $key, array $element) {
$pos = array_search($key, array_keys($form)) + 1;
return array_splice($form, 0, $pos - 1) + $element + $form;
}
/**
* Adds default element callbacks.
*
* This is a workaround where adding process and pre-render functions are not
* results in replacing the default ones instead of merging.
*
* @param array $element
* The render array for a single form element.
*
* @todo remove once the following issues are resolved.
* @see https://www.drupal.org/project/drupal/issues/2070131
* @see https://www.drupal.org/project/drupal/issues/2190333
*/
protected function addDefaultElementInfo(array &$element) {
/** @var \Drupal\Core\Render\ElementInfoManager $element_info_manager */
$element_info = \Drupal::service('element_info');
if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $element_info
->getInfo($element['#type']))) {
$element['#process'] = $element['#process'] ?? [];
$element['#pre_render'] = $element['#pre_render'] ?? [];
if (!empty($info['#process'])) {
$element['#process'] = array_merge($info['#process'], $element['#process']);
}
if (!empty($info['#pre_render'])) {
$element['#pre_render'] = array_merge($info['#pre_render'], $element['#pre_render']);
}
// Some processing needs to happen prior to the default form element
// callbacks (e.g. sort). We use the custom '#pre_process' array for this.
if (!empty($element['#pre_process'])) {
$element['#process'] = array_merge($element['#pre_process'], $element['#process']);
}
// Workaround to add support for #group FAPI to all elements currently not
// supported.
// @todo remove once core issue is resolved.
// @see https://www.drupal.org/project/drupal/issues/2190333
if (!in_array('processGroup', array_column($element['#process'], 1))) {
$element['#process'][] = [
'\\Drupal\\Core\\Render\\Element\\RenderElement',
'processGroup',
];
$element['#pre_render'][] = [
'\\Drupal\\Core\\Render\\Element\\RenderElement',
'preRenderGroup',
];
}
}
// Apply the same to any nested children.
foreach (Element::children($element) as $key) {
$child =& $element[$key];
$this
->addDefaultElementInfo($child);
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
BetterExposedFilters:: |
public | property | BEF filters widget plugin manager. | |
BetterExposedFilters:: |
protected | property | The module handler. | |
BetterExposedFilters:: |
public | property | BEF pager widget plugin manager. | |
BetterExposedFilters:: |
public | property | BEF sort widget plugin manager. | |
BetterExposedFilters:: |
protected | function | Adds default element callbacks. | |
BetterExposedFilters:: |
public static | function | The form ajax callback. | |
BetterExposedFilters:: |
public | function |
Build the views options form and adds custom options for BEF. Overrides InputRequired:: |
|
BetterExposedFilters:: |
public static | function |
Creates an instance of the plugin. Overrides PluginBase:: |
|
BetterExposedFilters:: |
protected | function | Creates a list of view handler default options. | |
BetterExposedFilters:: |
protected | function |
Information about options for all kinds of purposes will be held here.
@code
'option_name' => array( Overrides InputRequired:: |
|
BetterExposedFilters:: |
protected | function |
Overrides InputRequired:: |
|
BetterExposedFilters:: |
public | function |
Alters the exposed form. Overrides ExposedFormPluginBase:: |
|
BetterExposedFilters:: |
protected | function | Inserts a new form element before another element identified by $key. | |
BetterExposedFilters:: |
public | function |
Handle any special handling on the validate form. Overrides InputRequired:: |
|
BetterExposedFilters:: |
public | function |
Validate the options form. Overrides PluginBase:: |
|
BetterExposedFilters:: |
public | function |
BetterExposedFilters constructor. Overrides PluginBase:: |
|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
DependencySerializationTrait:: |
public | function | 1 | |
DependencySerializationTrait:: |
public | function | 2 | |
ExposedFormPluginBase:: |
protected | property |
Denotes whether the plugin has an additional options form. Overrides PluginBase:: |
|
ExposedFormPluginBase:: |
public | function |
Submits the exposed form. Overrides ExposedFormPluginInterface:: |
|
ExposedFormPluginBase:: |
public | function |
Validates the exposed form submission. Overrides ExposedFormPluginInterface:: |
|
ExposedFormPluginBase:: |
public | function |
The cache contexts associated with this object. Overrides CacheableDependencyInterface:: |
|
ExposedFormPluginBase:: |
public | function |
The maximum age for which this object may be cached. Overrides CacheableDependencyInterface:: |
|
ExposedFormPluginBase:: |
public | function |
The cache tags associated with this object. Overrides CacheableDependencyInterface:: |
|
ExposedFormPluginBase:: |
public | function |
Runs after the view has been executed. Overrides ExposedFormPluginInterface:: |
|
ExposedFormPluginBase:: |
public | function |
Runs after the view has been rendered. Overrides ExposedFormPluginInterface:: |
|
ExposedFormPluginBase:: |
public | function |
Runs before the view has been executed. Overrides ExposedFormPluginInterface:: |
|
ExposedFormPluginBase:: |
public | function |
Renders the exposed form. Overrides ExposedFormPluginInterface:: |
|
ExposedFormPluginBase:: |
public | function | Resets all the states of the exposed form. | |
InputRequired:: |
public | function |
Runs before the view is rendered. Overrides ExposedFormPluginBase:: |
|
InputRequired:: |
public | function |
Add anything to the query that we might need to. Overrides ExposedFormPluginBase:: |
|
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
public | property | Plugins's definition | |
PluginBase:: |
public | property | The display object this plugin is for. | |
PluginBase:: |
public | property | Options for this plugin will be held here. | |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
protected | property | Stores the render API renderer. | 3 |
PluginBase:: |
public | property | The top object of a view. | 1 |
PluginBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
14 |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Clears a plugin. Overrides ViewsPluginInterface:: |
2 |
PluginBase:: |
protected | function | Do the work to filter out stored options depending on the defined options. | |
PluginBase:: |
public | function |
Filter out stored options depending on the defined options. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public | function |
Returns an array of available token replacements. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function |
Returns the plugin provider. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
protected | function | Returns the render API renderer. | 1 |
PluginBase:: |
public | function |
Adds elements for available core tokens to a form. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public | function |
Returns a string with any core tokens replaced. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
constant | Include entity row languages when listing languages. | ||
PluginBase:: |
constant | Include negotiated languages when listing languages. | ||
PluginBase:: |
public | function |
Initialize the plugin. Overrides ViewsPluginInterface:: |
8 |
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
PluginBase:: |
protected | function | Makes an array of languages, optionally including special languages. | |
PluginBase:: |
public | function |
Return the human readable name of the display. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public static | function |
Moves form elements into fieldsets for presentation purposes. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public static | function |
Flattens the structure of form elements. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public static | function | Returns substitutions for Views queries for languages. | |
PluginBase:: |
protected | function | Fills up the options of the plugin with defaults. | |
PluginBase:: |
public | function |
Returns the summary of the settings in the display. Overrides ViewsPluginInterface:: |
6 |
PluginBase:: |
public | function |
Provide a full list of possible theme templates used by this style. Overrides ViewsPluginInterface:: |
1 |
PluginBase:: |
public static | function |
Lists the trusted callbacks provided by the implementing class. Overrides TrustedCallbackInterface:: |
6 |
PluginBase:: |
public | function |
Unpack options over our existing defaults, drilling down into arrays
so that defaults don't get totally blown away. Overrides ViewsPluginInterface:: |
|
PluginBase:: |
public | function |
Returns the usesOptions property. Overrides ViewsPluginInterface:: |
8 |
PluginBase:: |
public | function |
Validate that the plugin is correct and can be saved. Overrides ViewsPluginInterface:: |
6 |
PluginBase:: |
protected | function | Replaces Views' tokens in a given string. The resulting string will be sanitized with Xss::filterAdmin. | 1 |
PluginBase:: |
constant | Query string to indicate the site default language. | ||
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
TrustedCallbackInterface:: |
constant | Untrusted callbacks throw exceptions. | ||
TrustedCallbackInterface:: |
constant | Untrusted callbacks trigger silenced E_USER_DEPRECATION errors. | ||
TrustedCallbackInterface:: |
constant | Untrusted callbacks trigger E_USER_WARNING errors. |