custom_search-8.x-1.x-dev/src/Form/CustomSearchBlockForm.php
src/Form/CustomSearchBlockForm.php
<?php
namespace Drupal\custom_search\Form;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\Language;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\search\SearchPageRepositoryInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Component\Utility\Html;
/**
* Builds the search form for the search block.
*/
class CustomSearchBlockForm extends FormBase {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The search page repository.
*
* @var \Drupal\search\SearchPageRepositoryInterface
*/
protected $searchPageRepository;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity repository.
*
* @var \Drupal\Core\Entity\EntityRepositoryInterface
*/
protected $entityRepository;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The current path.
*
* @var \Drupal\Core\Path\CurrentPathStack
*/
protected $currentPath;
/**
* Constructs a new SearchBlockForm.
*
* @param \Drupal\search\SearchPageRepositoryInterface $search_page_repository
* The search page repository.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The Module handler object.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Path\CurrentPathStack $current_path
* The current path.
*/
public function __construct(
SearchPageRepositoryInterface $search_page_repository,
ModuleHandlerInterface $module_handler,
EntityTypeManagerInterface $entity_type_manager,
EntityRepositoryInterface $entity_repository,
LanguageManagerInterface $language_manager,
CurrentPathStack $current_path,
) {
$this->searchPageRepository = $search_page_repository;
$this->moduleHandler = $module_handler;
$this->entityTypeManager = $entity_type_manager;
$this->entityRepository = $entity_repository;
$this->languageManager = $language_manager;
$this->currentPath = $current_path;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('search.search_page_repository'),
$container->get('module_handler'),
$container->get('entity_type.manager'),
$container->get('entity.repository'),
$container->get('language_manager'),
$container->get('path.current'),
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'custom_search_block_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = func_get_arg(2);
$form['#method'] = 'post';
// Popup.
$form['popup'] = [
'#type' => 'fieldset',
'#weight' => 1 + $config['search_box']['weight'],
'#attributes' => ['class' => ['custom_search-popup']],
];
// Search box.
$form['keys'] = [
'#type' => 'search',
'#title' => Html::escape($config['search_box']['label']),
'#title_display' => $config['search_box']['label_visibility'] ? 'before' : 'invisible',
'#size' => $config['search_box']['size'],
'#maxlength' => $config['search_box']['max_length'],
'#default_value' => '',
'#placeholder' => ['title' => Html::escape($config['search_box']['placeholder'])],
'#attributes' => [
'title' => Html::escape($config['search_box']['title']),
'class' => ['custom_search-keys'],
],
'#weight' => $config['search_box']['weight'],
];
// Content.
$toptions = [];
$types = array_keys(array_filter($config['content']['types']));
if (count($types)) {
$names = node_type_get_names();
if (count($types) > 1 || $config['content']['any']['force']) {
$toptions['c-all'] = $config['content']['any']['text'];
}
foreach ($types as $type) {
$toptions['c-' . $type] = $names[$type];
}
}
$options = [];
// Other searches.
$others = !empty($config['content']['other']) ? array_keys(array_filter($config['content']['other'])) : [];
// If content types and other searches are combined, make an optgroup.
if (count($others) && count($toptions) && $config['content']['selector']['type'] == 'select') {
$search_page = $this->entityTypeManager->getStorage('search_page')->load($config['content']['page']);
$options[Html::escape($search_page->label())] = $toptions;
}
else {
$options = $toptions;
}
if (count($others)) {
$other_pages = $this->entityTypeManager->getStorage('search_page')->loadMultiple($others);
foreach ($others as $other) {
$options['o-' . Html::escape($other_pages[$other]->id())] = Html::escape($other_pages[$other]->label());
}
}
if (count($options)) {
$selector_type = $config['content']['selector']['type'];
if ($selector_type == 'selectmultiple') {
$selector_type = 'select';
$multiple = TRUE;
}
else {
$multiple = FALSE;
}
$form['types'] = [
'#type' => $selector_type,
'#multiple' => $multiple,
'#title' => Html::escape($config['content']['selector']['label']),
'#title_display' => $config['content']['selector']['label_visibility'] ? 'before' : 'invisible',
'#options' => $options,
'#default_value' => ($selector_type == 'checkboxes') ? ['c-all'] : 'c-all',
'#attributes' => ['class' => ['custom-search-selector', 'custom-search-types']],
'#weight' => $config['content']['weight'],
'#validated' => TRUE,
];
// If there's only one type, hide the selector.
if (count($others) + count($types) == 1 && !$config['content']['any']['force']) {
$form['custom_search_types']['#type'] = 'hidden';
$form['custom_search_types']['#default_value'] = key(array_slice($options, count($options) - 1));
}
elseif ($config['content']['region'] == 'popup') {
$form['popup']['types'] = $form['types'];
unset($form['types']);
}
}
// Taxonomy.
$vocabularies = $this->entityTypeManager->getStorage('taxonomy_vocabulary')->loadMultiple();
if (count($config['taxonomy']) > 0) {
$taxonomy_term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
foreach ($vocabularies as $voc) {
$vid = $voc->id();
if (empty($config['taxonomy'][$vid]['type']) || $config['taxonomy'][$vid]['type'] == 'disabled') {
continue;
}
$options = [];
$options['c-all'] = $config['taxonomy'][$vid]['all_text'];
$vocabulary_depth = (!$config['taxonomy'][$vid]['depth']) ? NULL : $config['taxonomy'][$vid]['depth'];
$terms = $taxonomy_term_storage->loadTree($vid, 0, $vocabulary_depth, TRUE);
foreach ($terms as $term) {
$termName = Html::escape($this->entityRepository->getTranslationFromContext($term)->label());
$options['c-' . $term->id()] = (mb_substr($config['taxonomy'][$vid]['type'], 0, 6) == 'select') ? str_repeat('-', $term->depth) . ' ' . $termName : $termName;
}
$selector_type = $config['taxonomy'][$vid]['type'];
if ($selector_type == 'selectmultiple') {
$selector_type = 'select';
$multiple = TRUE;
}
else {
$multiple = FALSE;
}
$form['vocabulary_' . $vid] = [
'#type' => $selector_type,
'#multiple' => $multiple,
'#title' => Html::escape($config['taxonomy'][$vid]['label']),
'#title_display' => $config['taxonomy'][$vid]['label_visibility'] ? 'before' : 'invisible',
'#options' => $options,
'#default_value' => ($selector_type == 'checkboxes') ? ['c-all'] : 'c-all',
'#attributes' => ['class' => ['custom-search-selector', 'custom-search-vocabulary']],
'#weight' => $config['taxonomy'][$vid]['weight'],
];
if ($config['taxonomy'][$vid]['region'] == 'popup') {
$form['popup']['vocabulary_' . $vid] = $form['vocabulary_' . $vid];
unset($form['vocabulary_' . $vid]);
}
}
}
// Languages.
$options = [];
$languages = array_keys(array_filter($config['languages']['languages']));
if (count($languages)) {
if (count($languages) > 1 || $config['languages']['any']['force']) {
$options['c-all'] = $config['languages']['any']['text'];
}
$current_language = $this->languageManager->getCurrentLanguage();
$current_language_id = $current_language->getId();
foreach ($languages as $language) {
switch ($language) {
case 'current':
$options['c-' . $language] = t('- Current language (@current) -', ['@current' => $current_language->getName()]);
break;
case Language::LANGCODE_NOT_SPECIFIED:
$options['c-' . $language] = t('- Not specified -');
break;
case Language::LANGCODE_NOT_APPLICABLE:
$options['c-' . $language] = t('- Not applicable -');
break;
default:
if ($language != $current_language_id || ($language != $current_language_id && !array_key_exists('c-' . $language, $options))) {
$options['c-' . $language] = $this->languageManager->getLanguageName($language);
}
}
}
}
if (count($options)) {
$selector_type = $config['languages']['selector']['type'];
if ($selector_type == 'selectmultiple') {
$selector_type = 'select';
$multiple = TRUE;
}
else {
$multiple = FALSE;
}
$form['languages'] = [
'#type' => $selector_type,
'#multiple' => $multiple,
'#title' => Html::escape($config['languages']['selector']['label']),
'#title_display' => $config['languages']['selector']['label_visibility'] ? 'before' : 'invisible',
'#options' => $options,
'#default_value' => ($selector_type == 'checkboxes') ? ['c-all'] : 'c-all',
'#attributes' => ['class' => ['custom-search-selector', 'custom-search-language']],
'#weight' => $config['languages']['weight'],
'#validated' => TRUE,
];
// If there's only one type, hide the selector.
if (count($languages) == 1 && !$config['languages']['any']['force']) {
$form['languages']['#type'] = 'hidden';
$form['languages']['#default_value'] = key(array_slice($options, count($options) - 1));
}
elseif ($config['languages']['region'] == 'popup') {
$form['popup']['languages'] = $form['languages'];
unset($form['languages']);
}
}
// Custom Paths.
$paths = $config['paths']['list'];
if ($paths != '') {
$options = [];
$lines = explode("\n", $paths);
foreach ($lines as $line) {
$temp = explode('|', $line);
$options[$temp[0]] = (count($temp) >= 2) ? t('@label', ['@label' => $temp[1]]) : '';
}
if (count($options) == 1) {
$form['paths'] = [
'#type' => 'hidden',
'#default_value' => key($options),
];
}
else {
$form['paths'] = [
'#type' => $config['paths']['selector']['type'],
'#title' => Html::escape($config['paths']['selector']['label']),
'#title_display' => $config['paths']['selector']['label_visibility'] ? 'before' : 'invisible',
'#options' => $options,
'#default_value' => key($options),
'#weight' => $config['paths']['weight'],
];
if ($config['paths']['region'] == 'popup') {
$form['popup']['paths'] = $form['paths'];
unset($form['paths']);
}
}
}
// Criteria.
$criteria = ['or', 'phrase', 'negative'];
foreach ($criteria as $c) {
if ($config['criteria'][$c]['display']) {
$form['criteria_' . $c] = [
'#type' => 'textfield',
'#title' => Html::escape($config['criteria'][$c]['label']),
'#size' => 15,
'#maxlength' => 255,
'#weight' => $config['criteria'][$c]['weight'],
];
if ($config['criteria'][$c]['region'] == 'popup') {
$form['popup']['criteria_' . $c] = $form['criteria_' . $c];
unset($form['criteria_' . $c]);
}
}
}
// Actions.
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => Html::escape($config['submit']['text']),
// Prevent op from showing up in the query string.
'#name' => '',
'#weight' => $config['submit']['weight'],
];
if ($config['submit']['image_path'] != '') {
$form['actions']['submit']['#type'] = 'image_button';
$form['actions']['submit']['#src'] = $config['submit']['image_path'];
$form['actions']['submit']['#attributes'] = [
'alt' => Html::escape($config['submit']['text']),
'class' => ['custom-search-button'],
];
unset($form['actions']['submit']['#value']);
}
elseif ($form['actions']['submit']['#value'] == '') {
$form['actions']['submit']['#attributes'] = ['style' => 'display:none;'];
}
// If nothing has been added to the popup, don't output any markup.
if (!count(Element::children($form['popup']))) {
unset($form['popup']);
}
// Add attributes.
$form['#attributes']['role'] = 'search';
// Passes the config for later use.
$form_state->set('config', $config);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $form_state->get('config');
$filters = [];
// Keywords.
$keys = trim($form_state->getValue('keys'));
// Filter Types.
$types = $form_state->hasValue('types') ? $form_state->getValue('types') : [];
if (!is_array($types)) {
$types = [$types];
}
// Check if we're using another search (ie. Users).
$first_type = current($types);
if (substr($first_type, 0, 2) == 'o-') {
$search_page_id = substr($first_type, 2);
$search_pages = $this->entityTypeManager->getStorage('search_page')->loadMultiple([$search_page_id]);
if (!empty($search_pages)) {
$route = 'search.view_' . $search_page_id;
}
}
else {
// Build route.
$route = 'search.view_' . $config['content']['page'];
// Types filters.
$types = array_map(function ($val) {
return $this->filterKeys($val);
}, array_filter($types));
$excluded = array_map(function ($val) {
return $this->filterKeys($val);
}, array_filter($config['content']['excluded']));
if (count($types)) {
if (in_array('all', $types)) {
// If - Any - is set to restrict the search, grab the content types.
if ($config['content']['any']['restricts']) {
$types = array_keys(array_filter($config['content']['types']));
}
// If exclusion has to be made, specify all the other types.
if (!empty($excluded)) {
$types = array_keys(node_type_get_names());
}
$types = array_diff($types, $excluded);
if (!in_array('all', $types)) {
foreach ($types as $type) {
$filters[] = 'type:' . $type;
}
}
}
// If it's not - Any -, search for that type.
else {
$types = array_diff($types, $excluded);
foreach ($types as $type) {
$filters[] = 'type:' . $type;
}
}
}
// If there's no type selector but exclusion has to be made, specify all
// the other types.
elseif (!empty($excluded)) {
$types = array_diff(array_keys(node_type_get_names()), $excluded);
foreach ($types as $type) {
$filters[] = 'type:' . $type;
}
}
// Taxonomy filters.
if ($this->moduleHandler->moduleExists('taxonomy')) {
$terms = [];
$vocabularies = $this->entityTypeManager->getStorage('taxonomy_vocabulary')->loadMultiple();
foreach ($vocabularies as $voc) {
$vid = $voc->id();
if ($form_state->hasValue('vocabulary_' . $vid)) {
$vterms = $form_state->getValue('vocabulary_' . $vid);
if (!is_array($vterms)) {
$vterms = [$vterms];
}
$terms = array_merge($terms, $vterms);
}
}
// Uses array_values() to filter here to get numerical index, so we can
// splice the array later if needed (see line below the array_map()).
$terms = array_map(function ($val) {
return $this->filterKeys($val);
}, array_values(array_filter($terms)));
// If one or more -Any- is selected, delete them.
while (($index = array_search('all', $terms)) !== FALSE) {
array_splice($terms, $index, 1);
}
if (count($terms)) {
foreach ($terms as $term) {
$filters[] = 'term:' . $term;
}
}
}
// Criteria filters.
if ($form_state->hasValue('criteria_or') && trim($form_state->getValue('criteria_or')) != '') {
$keys .= ' ' . str_replace(' ', ' OR ', trim($form_state->getValue('criteria_or')));
}
if ($form_state->hasValue('criteria_negative') && trim($form_state->getValue('criteria_negative')) != '') {
$keys .= ' -' . str_replace(' ', ' -', trim($form_state->getValue('criteria_negative')));
}
if ($form_state->hasValue('criteria_phrase') && trim($form_state->getValue('criteria_phrase')) != '') {
$keys .= ' "' . trim($form_state->getValue('criteria_phrase')) . '"';
}
// Language filters.
$languages = $form_state->hasValue('languages') ? $form_state->getValue('languages') : [];
if (!is_array($languages)) {
$languages = [$languages];
}
$languages = array_map(function ($val) {
return $this->filterKeys($val);
}, array_filter($languages));
if (count($languages)) {
// If - Any - is selected and - Any - is set to restrict the search,
// grab the languages.
if (in_array('all', $languages) && $config['languages']['any']['restricts']) {
$languages = array_keys(array_filter($config['languages']['languages']));
}
// If it's not - Any -, search for that language.
if (!in_array('all', $languages)) {
foreach ($languages as $language) {
if ($language == 'current') {
$filters[] = 'language:' . $this->languageManager->getCurrentLanguage()->getId();
}
else {
$filters[] = 'language:' . $language;
}
}
}
}
}
// Build a custom path if needed.
if ($form_state->hasValue('paths') && $form_state->getValue('paths') != '') {
$route = $form_state->getValue('paths');
$route = str_replace('[current_path]', $this->currentPath->getPath(), $route);
$route = str_replace('[key]', $keys, $route);
if (strpos($route, '[types]') !== FALSE) {
$route = str_replace('[types]', (isset($types) && count($types)) ? implode($config['paths']['separator'], $types) : '', $route);
}
if (strpos($route, '[terms]') !== FALSE) {
$route = str_replace('[terms]', (isset($terms) && count($terms)) ? implode($config['paths']['separator'], $terms) : '', $route);
}
// Check for a query string.
$query = [];
$route_query_position = strpos($route, '?');
if ($route_query_position !== FALSE) {
$query_tmp = substr($route, 1 + $route_query_position);
$query_tmp = str_replace('&', '&', $query_tmp);
$query_tmp = explode('&', $query_tmp);
foreach ($query_tmp as $param) {
$param_exploded = explode('=', $param);
$query[$param_exploded[0]] = $param_exploded[1];
}
$route = substr($route, 0, $route_query_position);
}
// If not an external URL, add the base url scheme.
if (substr($route, 0, 4) != 'http') {
$route = 'base://' . $route;
}
// Generate the final url.
$url = Url::fromUri($route, ['query' => $query]);
// Redirect.
if ($url->isExternal()) {
$form_state->setResponse(new TrustedRedirectResponse($url->toUriString(), 302));
}
else {
$form_state->setRedirectUrl($url);
}
}
else {
$query['keys'] = $keys;
if (count($filters)) {
$query['f'] = $filters;
}
$form_state->setRedirect(
$route,
[],
['query' => $query]
);
}
}
/**
* Helper functions.
*/
private static function filterKeys($val) {
return (strlen($val) > 2 && $val[1] == '-') ? mb_substr($val, 2) : $val;
}
}
