WordPress: Поиск по Сайту (без плагинов)

Сегодня разберёмся, как в WordPress реализовать AJAX поиск по сайту (так называемый «Живой поиск») без использования плагинов. Настроим его должным образом. Рассмотрим, как реализовать поиск по записям, страницам или произвольным типам записей. А также исключим определённые страницы/записи из поиска.

Содержание:

  1. код поиска WordPress (HTML, CSS);
  2. AJAX поиск (JavaScript);
  3. настройка поиска (functions.php);
    1. поиск по записям;
    2. поиск по страницам;
    3. поиск по произвольным типам записей;
  4. включения нужных типов записей в поиск;
    1. включение только записей в поиск;
    2. включение произвольного типа записей;
    3. включение нескольких типов записей в поиск
  5. исключение страниц/произвольных записей из поиска;
    1. исключение страниц по id;
    2. исключение кастомных типов записей;
    3. исключение категорий по id;
  6. включение поиск по метаполям;
  7. сортировка результатов поиска;
  8. подсветка результатов поиска;
  9. admin-ajax.php bad request 400.

Код поиска WordPress

Код поиска находится в файле searchform.php. Именно данный файл вам нужно выводить в том месте, где это необходимо. Чтобы вывести форму поиска WordPress, воспользуйтесь следующим кодом.

<?php get_search_form(); ?>

Ваш шаблон поиска searchform.php может выглядеть примерно так.

<form
    class="search-form"
    role="search"
    method="get"
    id="searchform"
    action="<?php echo home_url('/') ?>"
>
    <input
        class="search-form__input"
        type="text"
        value="<?php echo get_search_query() ?>"
        name="s" id="s"
        placeholder="Поиск на сайте WordPress"
        autocomplete="off"
    />
    <button type="submit" id="searchsubmit">
        <svg xmlns="http://www.w3.org/2000/svg" width="19.856" height="20.848" viewBox="0 0 19.856 20.848">
            <path d="M91.119,310.567l-4.713-4.713a8.8,8.8,0,0,0,2.51-6.147,8.708,8.708,0,1,0-8.708,8.708,8.983,8.983,0,0,0,5.02-1.588l4.815,4.815a.877.877,0,0,0,1.127,0A.792.792,0,0,0,91.119,310.567ZM73.037,299.708a7.171,7.171,0,1,1,7.171,7.171A7.192,7.192,0,0,1,73.037,299.708Z" transform="translate(-71.5 -291)" fill="#414544" />
        </svg>
    </button>
    <ul class="ajax-search"></ul>
</form>

Стили для выпадающего списка результатов поиска.

/* ajax search */

.search-form {
    position: relative;
}

.ajax-search {
    position: absolute;
    top: 100%;
    left: 0;
    width: 100%;
    right: 0;
    background: #fff;
    box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
    border-radius: 10px;
    margin-top: 5px;
    max-height: 230px;
    overflow-x: hidden;
    overflow-y: auto;
    z-index: 100;
    margin: 0;
    padding: 0;
    font-size: 14px;
    color: #424348;
    scrollbar-color: dark;
    scrollbar-width: thin;
    overscroll-behavior: contain;
}

.ajax-search::-webkit-scrollbar {
    width: 6px;
    background-color: #eff2f3;
}

.ajax-search::-webkit-scrollbar-thumb {
    background-color: #dddddd;
    border-radius: 4em;
}

.ajax-search__item {
    position: relative;
    border-top: 1px solid rgba(224, 229, 231, 0.5);
    padding: 10px 15px;
    cursor: pointer;
    list-style-type: none;
}

.ajax-search__link {
    color: var(--accent);
    line-height: 130%;
    margin-bottom: 10px;
    font-size: 13px;
    display: block;
}

.ajax-search__excerpt {
    cursor: default;
    font-size: 11px;
    line-height: 140%;
}

.ajax-search__not-found {
    font-size: 12px;
}

Живой поиск в WordPress

Для реализации живого поиска мы будем использовать технологию AJAX. Таким образом, поиск по сайту WordPress будет осуществляться без перезагрузки страницы.

При вводе текста мы будем отправлять AJAX запрос в php-обработчик (создадим его в следующем пункте данной статьи). При нахождении или не нахождении данных мы будем выводить пользователю результаты поиска в виде списка <ul class=»ajax-search»></ul>, который мы подготовили в предыдущем пункте статьи.

Создайте файл ajax-search.js или просто добавьте следующий JavaScript код в ваш главный javascript файл.

jQuery(document).ready(function ($) {
    const search_input = $(".search-form__input");
    const search_results = $(".ajax-search");

    search_input.keyup(function () {
        let search_value = $(this).val();

        if (search_value.length > 2) { // кол-во символов
            $.ajax({
                url: "/wp-admin/admin-ajax.php",
                type: "POST",
                data: {
                    "action": "ajax_search", // functions.php
                    "term": search_value
                },
                success: function (results) {
                    search_results.fadeIn(200).html(results);
                }
            });
        } else {
            search_results.fadeOut(200);
        }
    });

    // Закрытие поиска при клике вне его
    $(document).mouseup(function (e) {
        if (
            (search_input.has(e.target).length === 0) &&
            (search_results.has(e.target).length === 0)
        ) {
            search_results.fadeOut(200);
        };
    });
});

Если javascript файл вы создали отдельно, не забудьте подключить его в functions.php.

wp_enqueue_script(
    "{название_темы}-ajax-search",
    get_theme_file_uri("assets/js/ajax-search.js"), array(), "", true
);

Настройка поиска WordPress

При вводе в поле поиска отправляется AJAX запрос в функцию ajax_search. Создадим её в functions.php или в отдельном файле, как вам удобно. Я создам отдельно.

Создаю файл /functions/ajax-search.php и подключаю его в functions.php.

require get_template_directory() . "/functions/ajax-search.php"; # AJAX search

Сам код для этого файла.

<?php

// ajax поиск по сайту
add_action("wp_ajax_nopriv_ajax_search", "ajax_search");
add_action("wp_ajax_ajax_search", "ajax_search");
function ajax_search()
{
    $args = array(
        "post_type"      => "any", // Тип записи: post, page, кастомный тип записи
        "post_status"    => "publish",
        "order"          => "DESC",
        "orderby"        => "date",
        "s"              => $_POST["term"],
        "posts_per_page" => -1
    );
    $query = new WP_Query($args);
    if ($query->have_posts()) {
        while ($query->have_posts()) : $query->the_post();
            get_template_part("template-parts/loop-search-item");
        endwhile;
    } else {
        echo "Ничего не найдено"
    }
    exit;
}

Пример template-parts/loop-search-item.php:

<li class="ajax-search__item">
    <a href="<?php the_permalink(); ?>" class="ajax-search__link"><?php the_title(); ?></a>
    <div class="ajax-search__excerpt"><?php the_excerpt(); ?></div>
</li>

Обратите внимание на ключ post_type в массиве $args. В данном примере мы осуществляем поиск по всему сайту, т.к. мы использовали значение any. Рассмотрим и другие примеры поиска.

Количество результатов поиска

Чтобы вывести количество найденных результатов поиска, используйте код ниже:

$query->found_posts

WordPress поиск по записям

Чтобы выполнить поиск только по записям, измените значение any на post.

"post_type" => "post", // тип записи «Записи»
"post_type" => "page", // тип записи «Страницы»

Поиск по кастомным типам записей

Например, мы хотим выполнять поиск по страницам и по произвольному типу записей production, тогда используйте массив для множественного выбора.

"post_type" => array("page", "production"), // типы записей: «Страницы», «Продукция»

Включение типов записей в поиск

Обратите внимание, все следующие ниже фильтры ниже будут работать на странице поиска, для живого (AJAX) поиска, добавляйте нужные аргументы в свою функцию ajax_search (3 пункт данного руководства). Примеры:

"post_type" => array("page", "production"), # включение: «Страницы», «Продукция»

"post__in" => array(6, 11, 19), # включение страниц с id 6, 11, 19

"post__not_in" => array(36, 38), # исключение страниц с id 36, 38

"category__not_in" => array(6, 7), # исключение категорий с id 6, 7

Включение записей в поиск

Чтобы включить в поиск только записи, используйте код:

add_filter("pre_get_posts", "include_search_filter");
function include_search_filter($query)
{
    if (!is_admin() && $query->is_main_query() && $query->is_search) {
        $query->set("post_type", "post");
    }
    return $query;
}

Включение произвольного типа записей

Чтобы включить в поиск только произвольный тип записи production, используйте следующий код:

add_filter("pre_get_posts", "include_search_filter");
function include_search_filter($query)
{
    if (!is_admin() && $query->is_main_query() && $query->is_search) {
        $query->set("post_type", "production");
    }
    return $query;
}

Включение нескольких типов записей в поиск

Чтобы включить сразу несколько типов записей, используйте массив:

add_filter("pre_get_posts", "include_search_filter");
function include_search_filter($query)
{
    if (!is_admin() && $query->is_main_query() && $query->is_search) {
        $query->set("post_type", array("production", "page"));
    }
    return $query;
}

Таким образом, мы включили в поиск только «Страницы» и произвольный тип записи «Продукция».

Исключение страниц из поиска WordPress

Иногда может понадобиться исключить определённые страницы из поиска, рассмотрим, как это можно реализовать.

Исключение страниц из поиска по id

Чтобы исключить страницы с определёнными id, воспользуйтесь следующим кодом:

add_action("pre_get_posts", "my_exclude_search_filter");
function my_exclude_search_filter($query) {
    if (!$query->is_admin && $query->is_search && $query->is_main_query() ) {
        $query->set("post__not_in", array(36, 38) );
    }
}

Так мы исключим из поиска страницы с id 36 и 38.

Исключение произвольных типов записей из поиска

Чтобы исключить кастомный тип записи из поиска, используйте код:

add_action("init", "exclude_post_type_from_search", 99);
function exclude_post_type_from_search() {
    global $wp_post_types;

    if (post_type_exists("photo_gallery") ) {
        $wp_post_types["photo_gallery"]->exclude_from_search = true;
    }
}

Таким образом мы исключили тип записи photo_gallery из поиска.

Исключение категорий из поиска по id

Чтобы исключить определённые категории из поиска, воспользуйтесь кодом ниже.

add_filter("pre_get_posts","my_exclude_category_search_filter");
function my_exclude_category_search_filter($query) {
    if ($query->is_search) {
        $query->set("category__not_in", array(6, 7));
    }
    return $query;
}

Указав в массиве id 6 и 7, мы исключаем из поиска категории с этими id.

Включение в поиск метаполей

Добавим в стандартный поиск WordPress еще и поиск по метаполям.

add_filter('posts_join', 'cf_search_join');
add_filter('posts_where', 'cf_search_where');
add_filter('posts_distinct', 'cf_search_distinct');
function cf_search_join($join)
{
    global $wpdb;
    if (is_search())
        $join .= " LEFT JOIN $wpdb->postmeta ON ID = $wpdb->postmeta.post_id ";
    return $join;
}
function cf_search_where($where)
{
    global $wpdb;
    if (is_search()) {
        $where = preg_replace(
            "/\(\s*$wpdb->posts.post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
            "($wpdb->posts.post_title LIKE $1) OR ($wpdb->postmeta.meta_value LIKE $1)",
            $where
        );
    }
    return $where;
}
function cf_search_distinct($where)
{
    return is_search() ? 'DISTINCT' : $where;
}

Сортировка результатов поиска

Чтобы отсортировать результаты поиска по таксономии, используйте следующий код:

add_filter('posts_orderby', 'search_results_custom', 10, 2);
function search_results_custom($orderby, $query)
{
    global $wpdb;

    if (!is_admin() && is_search())
        $orderby =  $wpdb->prefix . "posts.post_type DESC";

    return  $orderby;
}

Таким образом результаты поиска будут отсортированы по типам записей. Можно добавить еще сортировку по заголовку:

add_filter('posts_orderby', 'search_results_custom', 10, 2);
function search_results_custom($orderby, $query)
{
    global $wpdb;

    if (!is_admin() && is_search())
        $orderby =  $wpdb->prefix . "posts.post_type DESC", {$wpdb->prefix}posts.post_title ASC";

    return  $orderby;
}

Так вы можете сортировать результаты нужным образом.

Подсветка результатов поиска

При переходе на страницу с результатами поиска, заголовок статьи и the_excerpt/the_content (отрывок или контент) будут подсвечены.

# Подсветка результатов поиска
add_filter("the_content", "search_results_hightlight");
add_filter("the_excerpt", "search_results_hightlight");
add_filter("the_title", "search_results_hightlight");
function search_results_hightlight($text)
{
    // цвета
    $styles = [
        "",
        "color: #000; background: #98fd65;",
        "color: #000; background: #ffcc56;",
        "color: #000; background: #98cefa;",
        "color: #000; background: #fd9897;",
        "color: #000; background: #df7dca;",
    ];

    // только для страницы поиска
    if (!is_search())
        return $text;

    $query_terms = get_query_var("search_terms");

    if (empty($query_terms))
        $query_terms = array_filter([get_query_var("s")]);

    if (empty($query_terms))
        return $text;

    $n = 0;
    foreach ($query_terms as $term) {
        $n++;

        $term = preg_quote($term, "/");
        $text = preg_replace_callback("/$term/iu", function ($match) use ($styles, $n) {
            return '<span style="' . $styles[$n] . '">"' . $match[0] . '"</span>';
        }, $text);
    }

    return $text;
}

Admin-ajax.php Bad Request 400

Если при вводе в поле поиска в консоли появляется ошибка 400 Bad Request, то ищите проблему в 3 пункте данного руководства. Вероятно, вы неправильно именовали экшены или создали отдельный файл и забыли подключить его (или неправильно подключили).

Думаю, логика использования AJAX поиска на сайте WordPress вам ясна. Если же что-то осталось не понятным, задавайте вопросы в комментариях.

Понравилась статья? — Можете поддержать проект в блоке ниже.