toolshed-8.x-1.x-dev/modules/toolshed_search/src/Form/ToolshedSearchFilterForm.php
modules/toolshed_search/src/Form/ToolshedSearchFilterForm.php
<?php
namespace Drupal\toolshed_search\Form;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\toolshed_search\Plugin\Block\ToolshedSearchBlock;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form class for creating a keyword search from an exposed view filter.
*/
class ToolshedSearchFilterForm extends FormBase {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected ModuleHandlerInterface $moduleHandler;
/**
* Create a new instance of the ToolshedSearchFilterForm class.
*
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
*/
public function __construct(ModuleHandlerInterface $module_handler) {
$this->moduleHandler = $module_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container): static {
return new static(
$container->get('module_handler')
);
}
/**
* {@inheritdoc}
*/
public function getFormId(): string {
return 'toolshed_search_filter_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, ?ToolshedSearchBlock $block = NULL): array {
/** @var \Drupal\views\ViewExecutable $view */
$view = $block->getView();
$config = $block->getConfiguration();
// Setup the form structure and elements.
// Determine the URL where the from should submit to.
switch ($config['action']) {
case 'view_url':
$actionUrl = $view->getUrl();
break;
case 'custom_url':
if ($config['custom_url']) {
$actionUrl = Url::fromUserInput($config['custom_url']);
break;
}
case 'current_page':
default:
$actionUrl = Url::fromRoute('<current>');
}
$form['#action'] = $actionUrl->toString();
// Prepare to build the views filters.
if (empty($config['use_exposed_input'])) {
$form_state->setUserInput([]);
}
if (!empty($config['grouped_filter'])) {
$form['#attached']['library'][] = 'toolshed_search/search-filters';
$form['#attributes']['data-target-view'] = $config['view'];
}
// Let form plugins know this is for exposed widgets.
$form_state->set('exposed', TRUE);
$this->moduleHandler->invokeAll('views_pre_build', [$view]);
$view->initHandlers();
// Create the filter elements for the form.
$form['filters'] = [];
foreach ($block->getFilters() as $name => $filterData) {
/** @var \Drupal\views\Plugin\views\filter\FilterPluginBase $filter */
$filter = $filterData['filter'];
if ($info = $filter->exposedInfo()) {
$filter->buildExposedForm($form['filters'], $form_state);
$form['#info']["filter-{$name}"] = $info;
$key = $filter->options['expose']['identifier'] ?? $info['value'] ?? '';
if (empty($key)) {
continue;
}
// Find a reference to the filter element.
if (isset($form['filters'][$key])) {
$element = &$form['filters'][$key];
}
elseif (isset($form['filters'][$key . '_wrapper'])) {
$element = &$form['filters'][$key . '_wrapper'][$key];
}
else {
continue;
}
// Some views filter plugins nest the value of the actual form element.
if (!empty($element['value'])) {
$element = &$element['value'];
}
if ($label = $filterData['label'] ?: $info['label']) {
$element['#title'] = $label;
$element['#title_display'] = $filterData['label_display'];
}
if ('textfield' === ($element['#type'] ?? '')) {
$element['#attributes']['autocomplete'] = 'off';
}
unset($element);
}
}
// Setup the search filter form submit button.
$buttonClasses = $config['submit_class_names'] ?? [];
$buttonClasses[] = 'button';
$buttonClasses[] = 'button--search';
// Create the button as a HTML button (instead of input submit) to allow
// the insertion of HTML as content, this includes easier icons.
$form['actions']['#type'] = 'actions';
$form['actions']['search'] = [
'#type' => 'html_button',
'#attributes' => [
'type' => 'submit',
'class' => $buttonClasses,
],
'content' => [
'#type' => 'html_tag',
'#tag' => 'span',
'#attributes' => [
'class' => ['button__text'],
],
'#plain_text' => $config['submit_label'],
],
];
if (!empty($config['reset_link'])) {
$form['actions']['reset_link'] = [
'#type' => 'link',
'#title' => $config['reset_link_text'] ?? $this->t('Reset filters'),
'#url' => $actionUrl,
'#attributes' => [
'class' => ['filter-reset-link'],
],
];
}
// If autocomplete configured, apply it to the search elements.
if ($autocomplete = $block->getAutocomplete()) {
$autocomplete->applyAutocomplete($form, $form['filters'], $block);
}
// Apply variant information to the form so theme suggestion and preprocess
// can utilize the variant data.
if (!empty($config['variant'])) {
$form['#block_variant'] = $config['variant'];
$form['actions']['search']['#attributes']['aria-label'] = new FormattableMarkup('@variant @label', [
'@label' => $config['submit_label'],
'@variant' => $config['variant'],
]);
}
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state): void {
// Form does uses GET (and doesn't use caching), so will not be processed.
// This means this method will never be called, and the actual processing
// will happen on the destination page.
}
}
