Пустой поисковый запрос возвращает все записи
Сегодня я решил добавить поисковую форму на свой сайт, которая должна возвращать результаты при поиске пользователем, но столкнулся с небольшой проблемой.
Моя проблема
Когда пользователь выполняет поиск, не вводя ничего в поисковую форму, по какой-то причине возвращается 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 ;-)
Pieter Goosen
Это действительно хорошая идея, останавливая основной запрос, она перенаправляет пользователей на страницу search.php, что мне и нужно, вместо возврата на страницу 404. Хотя метод @PieterGoosen отлично работает, если пользователи хотят перенаправления именно на страницу 404. Спасибо за дополнительную информацию, @birgire.
Stephen
@PieterGoosen Я так и сделал, хотя ваш ответ тоже отлично работает. Если кому-то нужно перенаправлять пустой поиск на страницу 404, то ваша идея подходит идеально :)
Stephen
@birgire у меня есть один вопрос по этому поводу - так как я использую это в своем коде, мне нравится понимать вещи, и я не совсем понимаю, что означает {, 10, 2);.
Stephen
фух, только что пришлось выйти на улицу в нынешнюю сумасшедшую погоду, просто перевожу дух, ;-) Это в основном касалось моей неудачной первоначальной идеи и было предназначено как дополнение к ответу от @PieterGoosen. CoderSte: я вообще настолько ленив, что часто использую анонимные функции на этом сайте ;-) Здесь 10 - это приоритет, а 2 - количество входных параметров callback-функции.
birgire
Идея верна, но последнее решение неверно.
empty( $search ) следует заменить на empty( get_search_query() ), так как $search содержит запрос, а не slug поиска. Поэтому условие if никогда не выполняется.
Gael.D
Я не уверен, является ли это преднамеренным багом или просто ошибкой, которую не предвидели, но это определенно недоработка в дизайне.
Такое поведение существует в следующих случаях, которые я отметил ранее:
Установка пустого массива для
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, как вы предложили, потому что это единственная страница с формой поиска.
Stephen
Довольно странно, на моей локальной установке возвращаются все записи из всех типов постов. Но в любом случае, WP_Query — это довольно запутанная штука, и иногда он ведёт себя очень странно. ;-) Не спеши и протестируй всё тщательно, дай мне знать, если найдёшь какие-то баги. Удачи! ;-)
Pieter Goosen
Я знаю, это очень странно. У меня есть около 7 страниц, у некоторых есть дочерние страницы, и дело в том, что возвращаются не только родительские страницы, а 2 родительские и 3 дочерние. Я проверю и дам вам знать, спасибо за ответ :)
Stephen
Что удивительно, каждый раз, когда я закрываю страницу и открываю её снова, а затем снова делаю пустой поиск, он возвращает разные страницы xD Я добавил ваш код в мой functions.php, и он сработал как чары, просто остаётся на странице 404 - есть ли способ отображать другой заголовок вместо "404", например, что-то вроде "Ваш поиск не дал результатов"? Спасибо за помощь, Pieter :)
Stephen
Если вы хотите исключить поисковые запросы короче определенной длины, а также пустые запросы, можно использовать модифицированную функцию из решения 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.
fbmd
Это может вызвать некорректную работу некоторых плагинов. 1) Во-первых, существует множество хуков, которые срабатывают при запуске страницы поиска. Некоторые плагины работают с запросом к БД, и главный запрос возвращает результаты (все записи), когда пользователь вводит пустой поиск. 2) Во-вторых, некоторые плагины, работающие с первым процессом, могут запутаться, когда главный запрос возвращает результаты, но страница результатов поиска не показывает никаких результатов, что приводит к их некорректной работе.
vee
Чтобы исключить поисковые запросы короче заданной длины, а также пустой поисковый запрос, вы можете использовать модифицированную функцию решения 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 );