Пошук на сайті – річ, яку помічаєш лише тоді, коли він працює погано. Коли я робив свою першу тему, то вставив форму пошуку в шапку, скопіював index.php у search.php і дописав зверху рядок «знайдено N збігів». Працювало, але той рядок робив зайвий запит до бази, а функції в ньому давно прибрали з WordPress. Тож розповім, як сьогодні зробити в темі нормальну форму пошуку і сторінку результатів: з кількістю знайденого, підсвіткою запиту і без зайвих запитів.
Два файли: форма і результати
За пошук у темі відповідають два файли:
- searchform.php – сама форма: поле і кнопка;
- search.php – сторінка з результатами.
Обидва необов'язкові. Без searchform.php WordPress виведе свою стандартну форму, а без search.php результати покаже файл index.php, як і будь-яку іншу сторінку, для якої немає окремого шаблону. Але свої файли дають повний контроль над розміткою, тож я роблю їх завжди.
Форма пошуку: searchform.php
<form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>">
<label>
<span class="screen-reader-text"><?php esc_html_e( 'Search for:', 'mytheme' ); ?></span>
<input type="search" class="search-field" name="s"
value="<?php echo get_search_query(); ?>"
placeholder="<?php esc_attr_e( 'Search…', 'mytheme' ); ?>">
</label>
<button type="submit" class="search-submit"><?php esc_html_e( 'Search', 'mytheme' ); ?></button>
</form>
Що тут важливо:
- name="s" – саме за параметром
sWordPress розуміє, що це пошук (/?s=слово). Колись я писав, що його можна міняти на свій розсуд, – ні, не можна: з іншою назвою пошук просто не спрацює. - action веде на головну сторінку сайту, а
home_url()підставить правильну адресу навіть після переїзду на інший домен. - get_search_query() повертає те, що людина шукала, вже екранованим, тож після пошуку запит лишається в полі і його можна підправити.
- type="search" замість
text– на телефоні з'явиться клавіатура з кнопкою «Пошук», а в полі – хрестик для очищення. - label. Підказка в
placeholderзникає, щойно починаєш друкувати, і програми для незрячих не завжди її читають. Тому поле загорнуте в<label>з текстом, який видно лише програмам читання (класscreen-reader-textховає його візуально, стилі для нього є в кожній стандартній темі WordPress). - Без id. Форма часто буває на сторінці двічі: у шапці і на сторінці «нічого не знайдено». Однакові
idна одній сторінці – помилка, тож лише класи.
Вивести форму можна будь-де одним рядком, наприклад у header.php:
<?php get_search_form(); ?>
Ця функція сама знайде searchform.php у темі. Якщо ж ти не хочеш писати свою форму, а стандартна тебе влаштовує, додай у functions.php підтримку HTML5-розмітки, щоб WordPress видавав сучасну форму:
add_theme_support( 'html5', array( 'search-form' ) );
Сторінка результатів: search.php
<?php get_header(); ?>
<main id="main" class="site-main">
<?php if ( have_posts() ) : ?>
<header class="page-header">
<h1 class="page-title">
<?php
printf(
esc_html( _n( '%1$s result for “%2$s”', '%1$s results for “%2$s”', $wp_query->found_posts, 'mytheme' ) ),
number_format_i18n( $wp_query->found_posts ),
get_search_query()
);
?>
</h1>
</header>
<?php while ( have_posts() ) : the_post(); ?>
<?php get_template_part( 'template-parts/content' ); ?>
<?php endwhile; ?>
<?php the_posts_pagination(); ?>
<?php else : ?>
<?php get_template_part( 'template-parts/content', 'none' ); ?>
<?php endif; ?>
</main>
<?php get_footer(); ?>
Будова та сама, що в index.php і в шаблоні категорії, відрізняється лише заголовок.
Скільки знайдено
Старий код рахував результати окремим запитом, який вибирав з бази всі знайдені записи без обмеження. На великому сайті це повільно, і головне – зайве: WordPress уже порахував їх, щоб зробити посторінкову навігацію. Кількість лежить у $wp_query->found_posts, лишається тільки вивести.
Функція _n() вибирає правильну форму слова залежно від числа. В англійській форм дві (result, results), а в українській три: «1 результат», «2 результати», «5 результатів». Усі три вписуються у файл перекладу теми, і WordPress сам підставить потрібну, – як це налаштувати, читай у статті як підготувати шаблон WordPress для перекладу. А number_format_i18n() покаже велике число з пробілом між тисячами, як прийнято в українській.
Нічого не знайдено
Якщо результатів немає, підключається template-parts/content-none.php, який ми зробили разом з index.php. Він показує повідомлення і ще раз форму пошуку, щоб людина могла одразу спробувати інший запит, а не тиснула «Назад».
Підсвітка запиту в результатах
Приємна дрібниця: виділити шукане слово в назвах і уривках, щоб одразу було видно, чому знайшовся саме цей запис. Додаємо у functions.php:
function mytheme_highlight_search( $text ) {
if ( ! is_search() || ! in_the_loop() ) {
return $text;
}
$words = preg_split( '/\s+/u', get_search_query( false ), -1, PREG_SPLIT_NO_EMPTY );
foreach ( $words as $word ) {
if ( mb_strlen( $word ) < 3 ) {
continue;
}
$text = preg_replace( '/(' . preg_quote( $word, '/' ) . ')(?![^<]*>)/iu', '<mark>$1</mark>', $text );
}
return $text;
}
add_filter( 'the_title', 'mytheme_highlight_search' );
add_filter( 'the_excerpt', 'mytheme_highlight_search' );
Що тут відбувається:
- Функція працює лише на сторінці пошуку і лише всередині циклу, щоб не зачепити меню чи назви записів у бічній колонці.
- Запит розбивається на слова, а занадто короткі (до 3 літер) пропускаються – інакше прийменник «на» підсвітився б у половині тексту.
- Кожне слово загортається в тег
<mark>без урахування регістру. Шматок(?![^<]*>)не дає замінити текст усередині HTML-тегів, щоб випадково не зламати розмітку.
Браузер за замовчуванням малює <mark> жовтим фоном, але його легко підлаштувати під свій дизайн:
.search-results mark {
background: #fff3b0;
color: inherit;
padding: 0 0.1em;
}
Клас search-results сам додається до <body> на сторінці пошуку, тож стиль не вплине на інші <mark> на сайті.
Що саме шукати
За замовчуванням WordPress шукає і в записах, і в сторінках. Якщо сторінки на кшталт «Контакти» чи «Політика конфіденційності» тобі в результатах не потрібні, обмеж пошук лише записами:
function mytheme_search_only_posts( $query ) {
if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
$query->set( 'post_type', 'post' );
}
}
add_action( 'pre_get_posts', 'mytheme_search_only_posts' );
Хук pre_get_posts змінює запит ще до того, як WordPress піде в базу. Так само можна змінити кількість результатів на сторінці чи їхній порядок – детальніше про цей хук я писав у статті правильне сортування статей у WordPress.
І чесно про обмеження. Вбудований пошук просто шукає збіг тексту в назві, уривку і вмісті, ставлячи вище записи, у назві яких є запит. Відмінків він не розуміє: на запит «пісня» не знайде «пісні». Для невеликого блогу цього вистачає, а якщо сайт великий і пошук для нього важливий, тоді вже варто дивитися в бік спеціальних плаґінів.
Що далі
Залишився останній обов'язковий шматок класичної теми – низ сторінки: ось як зробити файл footer.php. А якщо тему ти робиш з нуля і ще не бачив/ла всієї картини, почни зі статті як створити тему WordPress з нуля.
Більше про це – за позначками Створення тем WordPress та PHP.
А на твоєму сайті пошуком хтось узагалі користується? Пиши в коментарях 🙂


Прокоментувати
На сайті відображається лише твоє ім'я та коментар. Електронна пошта зберігається виключно для зв'язку з тобою за потреби та в жодному разі не передається стороннім особам.