Пустой поисковый запрос возвращает все записи

4 февр. 2016 г., 14:18:01
Просмотры: 22K
Голосов: 9

Сегодня я решил добавить поисковую форму на свой сайт, которая должна возвращать результаты при поиске пользователем, но столкнулся с небольшой проблемой.

Моя проблема

Когда пользователь выполняет поиск, не вводя ничего в поисковую форму, по какой-то причине возвращается 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>
2
Комментарии

Похоже на баг. Я разберусь и попробую отследить его ;-)

Pieter Goosen Pieter Goosen
4 февр. 2016 г. 17:58:06

Публикую ответ прямо сейчас

Pieter Goosen Pieter Goosen
4 февр. 2016 г. 18:20:00
Все ответы на вопрос 6
9
12

Просто альтернатива информативному ответу от @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 );
4 февр. 2016 г. 20:59:47
Комментарии

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

Pieter Goosen Pieter Goosen
4 февр. 2016 г. 21:09:41

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

Stephen Stephen
4 февр. 2016 г. 21:09:43

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

Stephen Stephen
4 февр. 2016 г. 21:13:35

@CoderSte отлично, наслаждайтесь и удачи в вашем проекте ;-)

Pieter Goosen Pieter Goosen
4 февр. 2016 г. 21:15:32

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

Stephen Stephen
4 февр. 2016 г. 21:18:17

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

birgire birgire
4 февр. 2016 г. 21:25:26

О, спасибо за разъяснение, теперь мне всё понятно :)

Stephen Stephen
5 февр. 2016 г. 10:41:11

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

Gael.D Gael.D
24 апр. 2019 г. 15:37:09

@Gael.D Спасибо за комментарий, пожалуйста, не стесняйтесь редактировать и исправлять ответ по мере необходимости, спасибо.

birgire birgire
24 апр. 2019 г. 21:25:02
Показать остальные 4 комментариев
5

Я не уверен, является ли это преднамеренным багом или просто ошибкой, которую не предвидели, но это определенно недоработка в дизайне.

Такое поведение существует в следующих случаях, которые я отметил ранее:

  • Установка пустого массива для 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();
            });
        }
    }
});
4 февр. 2016 г. 18:48:04
Комментарии

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

Stephen Stephen
4 февр. 2016 г. 19:13:48

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

Pieter Goosen Pieter Goosen
4 февр. 2016 г. 19:17:45

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

Stephen Stephen
4 февр. 2016 г. 19:19:23

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

Stephen Stephen
4 февр. 2016 г. 20:03:15

Сделаю так после того, как попробую сделать это сам, а Google не даст мне ответов на это xD

Stephen Stephen
4 февр. 2016 г. 20:06:34
0

Если вы хотите исключить поисковые запросы короче определенной длины, а также пустые запросы, можно использовать модифицированную функцию из решения 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 );
19 февр. 2020 г. 17:02:59
2

Если вы используете стандартный файл search.php, гораздо проще просто добавить логику в условие if ( have_posts())...

if ( have_posts() && $_GET['s'] != '' )

Таким образом, если параметр поиска пуст, цикл вывода контента не выполняется.

11 янв. 2021 г. 19:44:38
Комментарии

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

fbmd fbmd
12 мар. 2021 г. 11:40:59

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

vee vee
8 мая 2024 г. 11:32:13
0

Чтобы исключить поисковые запросы короче заданной длины, а также пустой поисковый запрос, вы можете использовать модифицированную функцию решения 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 );
11 июл. 2020 г. 08:22:13
0

Дополняя ответ Чада, вы также можете предотвратить поиск по пустым строкам, что в моём случае тоже возвращало результаты:

if ( have_posts() && $_GET['s'] !== '' && !ctype_space($_GET['s']) )
18 авг. 2022 г. 05:10:44