Пустой поисковый запрос возвращает все записи
Сегодня я решил добавить поисковую форму на свой сайт, которая должна возвращать результаты при поиске пользователем, но столкнулся с небольшой проблемой.
Моя проблема
Когда пользователь выполняет поиск, не вводя ничего в поисковую форму, по какой-то причине возвращается 5 страниц, хотя я хотел бы, чтобы выводилось сообщение вроде 'Поисковое поле было пустым - Повторите поиск'.
Как можно это реализовать? Ниже я привел код, который управляет кнопкой searchform
и выводом результатов поиска.
Код управления поисковой формой
searchform.php Это поисковая форма, куда пользователь вводит запрос (думаю, здесь нужно добавить код для проверки на пустое значение)
<form role="search" method="get" id="searchform" action="<?php echo home_url('/'); ?>">
<div>
<label class="screen-reader-text" for="s">Поиск: </label>
<input type="text" value="" name="s" id="s" placeholder="<?php the_search_query(); ?>" />
<input type="submit" id="searchsubmit" value="Поиск" />
</div>
</form>
search.php Здесь показывается пользователю, что они искали, и подключается шаблон content-search
для отображения названия страниц(ы).
<div class="search-result">
<div class="container">
<div class="row">
<div class="search">
<?php if (have_posts()) : ?>
<h2>Результаты поиска для: <?php the_search_query(); ?></h2>
<div class="light-separator small center"></div>
<?php
while (have_posts()) : the_post();
get_template_part('content-search', get_post_format());
endwhile;
else :
echo '
<div class="no-content">
<h3>Упс, похоже, ничего не соответствует вашему запросу.</h3>
</div>';
endif;
?>
<div class="search-form"><?php get_search_form(); ?></div>
</div>
</div>
</div>
</div>
content-search.php - Отображает заголовок страницы и постоянную ссылку на страницу
<div class="searchs">
<h4>Страницы - <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h4>
</div>

Просто альтернатива информативному ответу от @PieterGoosen.
После того как Pieter опубликовал эту часть:
if ( ! empty( $q['s'] ) ) {
$search = $this->parse_search( $q );
}
мне пришло в голову, что можно попробовать переразобрать поисковый запрос внутри фильтра posts_search
для пустой строки поиска. Но метод parse_search()
является защищенным, и даже если бы мы могли получить к нему доступ, пустой поиск просто вернул бы нам:
AND (((wp_posts.post_title LIKE '%%') OR (wp_posts.post_content LIKE '%%')))
что просто выполняло бы поиск по всему. Так что этот путь не был продуктивным ;-)
В WordPress 4.5 это изменилось на:
if ( strlen( $q['s'] ) {
$search = $this->parse_search( $q );
}
Вместо этого мы можем попробовать остановить основной запрос в случае пустого поиска:
/**
* Остановить основной запрос в случае пустого поиска
*/
add_filter( 'posts_search', function( $search, \WP_Query $q )
{
if( ! is_admin() && empty( $search ) && $q->is_search() && $q->is_main_query() )
$search .=" AND 0=1 ";
return $search;
}, 10, 2 );

@CoderSte Думаю, это решение лучше справится с вашей проблемой, чем мой подход. Не стесняйтесь отменить принятие моего ответа и принять этот от birgire ;-)

Это действительно хорошая идея, останавливая основной запрос, она перенаправляет пользователей на страницу search.php
, что мне и нужно, вместо возврата на страницу 404. Хотя метод @PieterGoosen отлично работает, если пользователи хотят перенаправления именно на страницу 404. Спасибо за дополнительную информацию, @birgire.

@PieterGoosen Я так и сделал, хотя ваш ответ тоже отлично работает. Если кому-то нужно перенаправлять пустой поиск на страницу 404, то ваша идея подходит идеально :)

@birgire у меня есть один вопрос по этому поводу - так как я использую это в своем коде, мне нравится понимать вещи, и я не совсем понимаю, что означает {, 10, 2);
.

фух, только что пришлось выйти на улицу в нынешнюю сумасшедшую погоду, просто перевожу дух, ;-) Это в основном касалось моей неудачной первоначальной идеи и было предназначено как дополнение к ответу от @PieterGoosen. CoderSte: я вообще настолько ленив, что часто использую анонимные функции на этом сайте ;-) Здесь 10 - это приоритет, а 2 - количество входных параметров callback-функции.

Идея верна, но последнее решение неверно.
empty( $search )
следует заменить на empty( get_search_query() )
, так как $search
содержит запрос, а не slug поиска. Поэтому условие if никогда не выполняется.

Я не уверен, является ли это преднамеренным багом или просто ошибкой, которую не предвидели, но это определенно недоработка в дизайне.
Такое поведение существует в следующих случаях, которые я отметил ранее:
Установка пустого массива для
post__in
возвращает все записи.Передача недопустимого термина в
tax_query
или использование поляname
с именем, содержащим специальные символы или более одного слова, удаляет JOIN-условие из SQL-запроса, что также приводит к возврату всех записей. Я дал ответ по этой проблеме.
Итак, что происходит здесь: когда мы передаем валидную строку в нашу функцию поиска, условие WHERE
изменяется, чтобы включить наши поисковые термины. Обычно условие WHERE
выглядит так, когда мы вводим поисковый термин search
:
AND (((wp_posts.post_title LIKE \'%search%\')
OR (wp_posts.post_content LIKE \'%search%\')))
AND wp_posts.post_type IN (\'post\', \'page\', \'attachment\', \'information\', \'event_type\', \'cameras\')
AND (wp_posts.post_status = \'publish\'
OR wp_posts.post_author = 1
AND wp_posts.post_status = \'private\')
Когда мы передаем пустую строку, условие поиска удаляется из WHERE
, что приводит к возврату всех записей. Вот как выглядит условие WHERE
, когда мы передаем пустую строку:
AND wp_posts.post_type IN (\'post\', \'page\', \'attachment\', \'information\', \'event_type\', \'cameras\')
AND (wp_posts.post_status = \'publish\' OR wp_posts.post_author = 1
AND wp_posts.post_status = \'private\')
Этот раздел в WP_Query
ответственный за такое поведение:
if ( ! empty( $q['s'] ) ) {
$search = $this->parse_search( $q );
}
Самый простой способ избежать этого — возвращать ошибку 404 всякий раз, когда мы передаем пустую строку в качестве поисковых терминов. Для этого нам нужно проверить, есть ли валидная поисковая строка, и затем установить 404 в зависимости от этого. Вы можете попробовать следующее:
add_action( 'pre_get_posts', function ( $q )
{
if( !is_admin() // Только для фронтенда
&& $q->is_main_query() // Только для основного запроса
&& $q->is_search() // Только для страницы поиска
) {
// Получаем поисковые термины
$search_terms = $q->get( 's' );
// Устанавливаем 404, если s пусто
if ( !$search_terms ) {
add_action( 'wp', function () use ( $q )
{
$q->set_404();
status_header(404);
nocache_headers();
});
}
}
});

Спасибо за ответ - это действительно странно, так как на сайте около 15 страниц, а возвращается только 5, и те 5, что возвращаются, выбираются совершенно случайно. Я ещё не проверял ваш код, так как не за компьютером, но когда доберусь до него, обязательно попробую. По сути, мне нужно, чтобы возвращалась страница 404
, как вы предложили, потому что это единственная страница с формой поиска.

Довольно странно, на моей локальной установке возвращаются все записи из всех типов постов. Но в любом случае, WP_Query
— это довольно запутанная штука, и иногда он ведёт себя очень странно. ;-) Не спеши и протестируй всё тщательно, дай мне знать, если найдёшь какие-то баги. Удачи! ;-)

Я знаю, это очень странно. У меня есть около 7 страниц, у некоторых есть дочерние страницы, и дело в том, что возвращаются не только родительские страницы, а 2 родительские и 3 дочерние. Я проверю и дам вам знать, спасибо за ответ :)

Что удивительно, каждый раз, когда я закрываю страницу и открываю её снова, а затем снова делаю пустой поиск, он возвращает разные страницы xD Я добавил ваш код в мой functions.php
, и он сработал как чары, просто остаётся на странице 404 - есть ли способ отображать другой заголовок вместо "404", например, что-то вроде "Ваш поиск не дал результатов"? Спасибо за помощь, Pieter :)

Если вы хотите исключить поисковые запросы короче определенной длины, а также пустые запросы, можно использовать модифицированную функцию из решения birgire. В этом примере пустые запросы и запросы из одной буквы не дадут результатов:
add_filter( 'posts_search', function( $search, \WP_Query $q )
{
$sphrase = get_search_query(); // Получаем поисковый запрос
$slen = strlen($sphrase); // Определяем длину запроса
$minlen = 2; // Минимальная допустимая длина
if( ! is_admin() && $slen < $minlen && $q->is_search() && $q->is_main_query() ){
$search .=" AND 0=1 "; // Исключаем результаты, если запрос слишком короткий
}
return $search;
}, 10, 2 );

Гораздо проще в реализации, чем решение с add_filter
, и прекрасно работает с кастомным search.php
.

Это может вызвать некорректную работу некоторых плагинов. 1) Во-первых, существует множество хуков, которые срабатывают при запуске страницы поиска. Некоторые плагины работают с запросом к БД, и главный запрос возвращает результаты (все записи), когда пользователь вводит пустой поиск. 2) Во-вторых, некоторые плагины, работающие с первым процессом, могут запутаться, когда главный запрос возвращает результаты, но страница результатов поиска не показывает никаких результатов, что приводит к их некорректной работе.

Чтобы исключить поисковые запросы короче заданной длины, а также пустой поисковый запрос, вы можете использовать модифицированную функцию решения birgire:
add_filter( 'posts_search', function( $search, \WP_Query $q )
{
$sphrase = get_search_query();
$slen = strlen($sphrase);
$minlen = 2;
if( ! is_admin() && $slen < $minlen && $q->is_search() && $q->is_main_query() ){
$search .=" AND 0=1 ";
}
return $search;
}, 10, 2 );
