Создаем свой собственный поиск в WordPress
Функция поиска в WordPress имеет плохую репутацию, потому существует масса различных плагинов, которые позволяет внести определенные усовершенствования в нее, однако они не всегда предлагают то, что вам требуется, особенно если вы хотите создать дополнительную систему поиска под некоторые фиксированные требования.
В данной статье мы посмотрим на то, как создать свой собственный поиск в WordPress, и заодно раскроем некоторые связанные с этим секреты.
Несмотря на многочисленные плагины, которые позволяют улучшить различные аспекты поиска WordPress, начиная с упорядочивания по релевантности и заканчивая включением произвольных полей в поиск, бывают такие ситуации, когда плагины, причем в любых своих комбинациях, не способны выполнить именно того, что вам требуется, и в итоге вам приходится писать свой собственный код.
Улучшаем поиск в WordPress путем добавления расширенной поисковой формы
За поиск WordPress отвечает класс WP_Query. Если вы раньше работали с произвольными циклами, то вы имеете представление о WP_Query.
WP_Query имеет массу возможных параметров, многие из которых могут быть легко определены в поисковой форме (или непосредственно в URL), что изменит поведение поиска.
К примеру, чтобы превратить обычный поиск в поиск по произвольному типу записей с названием product, достаточно ввести следующее:
http://www.yoursite.com/?s=football&post_type=product
В итоге мы получим результаты для записей с типом product, которые содержат в своем заголовке или контенте слово football.
Если вы пробежитесь по списку параметров WP_Query (он довольно обширный), то обнаружите много строковых и целочисленных параметров, которые вы можете легко закодировать в URL, чтобы изменить поведение поиска, начиная от добавления или исключения категорий и заканчивая добавлением поиска по таксономии и ограничением поиска по определенным авторам.
Помните, что для поиска важно включать параметр s в URL.
Существует также много дополнительных параметров, с помощью которых можно полностью изменить поведение, но которые, увы, не задокументированы в кодексе.
Поиск по фразе
По умолчанию WordPress совершает поиск по ключевому слову. Это означает, что если в поиск передан запрос «football boots», то WordPress формирует следующий код для условия WHERE:
((wp_posts.post_title LIKE '%football%') OR (wp_posts.post_content LIKE '%football%')) AND ((wp_posts.post_title LIKE '%boots%') OR (wp_posts.post_content LIKE '%boots%'))
Как вы можете видеть, здесь задан не поиск целой фразы, а поиск отдельных слов – «football» в заголовке или контенте и «boots» в заголовке или контенте. Таким образом, запись, содержащая слово «boot» в заголовке и «football» в контенте не будет, очевидно, соответствовать тому, что хотел получить пользователь, однако именно она будет выдана на экран.
Вы можете, правда, сделать так, чтобы WordPress искал фразы – для этого достаточно добавить sentence=1 в URL, что приведет к изменению условия WHERE:
((wp_posts.post_title LIKE '%football boots%') OR (wp_posts.post_content LIKE '%football boots%'))
Теперь поиск ведется по фразе, поэтому заголовок или контент материала должен фактически содержать оба слова (не обязательно подряд), чтобы быть найденным и выданным на экран. Попробуйте это сделать на своем собственном сайте. Запустите обычный поиск, после чего добавьте &sentence=1 к URL, и вы увидите, в чем состоит разница.
Поиск точного совпадения
Связанным с sentence, однако более специфичным является параметр exact. Добавление exact=1 к URL приведет к следующим изменениям в условии WHERE:
((wp_posts.post_title LIKE 'football boots') OR (wp_posts.post_content LIKE 'football boots'))
Вместо того чтобы разыскивать отличия, я сразу скажу вам, что единственная разница между условиях sentence и exact заключается в удалении % вокруг фразы в операторах LIKE. Такое удаление имеет большое значение, поскольку теперь заголовок или контент должен точно соответствовать поисковому запросу, а не просто включать его в себя.
То есть, если ни один продукт не имеет в заголовке фразы «football boots», то никаких результатов выдано не будет. Использовать exact нужно очень осторожно.
Изменение поисковой формы
Стандартная поисковая форма в WordPress довольно проста:
<form role="search" method="get" id="searchform" class="searchform" action="http://www.test.dev/"> <div> <label class="screen-reader-text" for="s">Search for:</label> <input type="text" value="" name="s" id="s" /> <input type="submit" id="searchsubmit" value="Search" /> </div> </form>
Если вы хотите изменить поисковое поведение, то в таком случае вам нужно добавить ваши собственные поля в форму.
<form role="search" method="get" id="searchform" action="http://www.test.dev/"> <div> <label for="s">Search for:</label> <input type="text" value="" name="s" id="s" /> <input type="hidden" value="1" name="sentence" /> <input type="hidden" value="product" name="post_type" /> <input type="submit" id="searchsubmit" value="Search" /> </div> </form>
Эта поисковая форма при отправлении будет генерировать следующий URL:
http://www.test.dev/?s={query}&sentence=1&post_type=product
Она по-прежнему будет вызывать стандартную страницу поисковых результатов, однако эти результаты будут уже для произвольного типа записей product, причем выдаваемые записи должны будут содержать поисковую фразу либо в своем заголовке, либо в контенте.
Самый простой способ создать свою собственную поисковую форму, предполагая, что вы хотите оставить стандартную форму в своем изначальном виде – это создать новый шаблон страницы с поисковой формой, закодированной под ваши требования, и привязать его к определенной странице.
Если же вы хотите обновить дефолтную поисковую форму – чтобы запустить поиск по фразам, к примеру – то в таком случае у вас есть два варианта, как поступить; в данном случае мы будем предполагать, что в вашем шаблоне поисковая форма не является жестко кодированной:
- Первый вариант – поместить произвольную поисковую форму в шаблон searchform.php. Всякий раз, когда функция get_search_form() будет вызвана, она в первую очередь будет использовать данный шаблон.
- Второй вариант – использовать фильтр get_search_form, что вынудит WordPress обратиться к вашей произвольной форме поиска.
Обе эти техники в деталях описаны в кодексе.
Когда произвольной формы недостаточно
Несмотря на то что с помощью произвольной формы поиска можно сделать очень и очень многое, ее порой бывает недостаточно. Существуют такие случаи, когда вам нужно создать свой WP_Query и обработать результаты самостоятельно – в частности, когда вы добавляете дополнительную функцию поиска.
Конкретный пример
Наш пример будет касаться коммерческого сайта компании, которая продает некую смесь физических и цифровых товаров. Большая часть цифровых товаров – это прошлые номера двух журналов, которые выпускались вместе с буклетами в цифровом и печатном формате.
Компания хотела бы реализовать поиск по библиотеке, который позволит посетителям искать только журналы и буклеты по определенным фразам. На сайте уже был реализован поиск по продуктам, однако результаты были не слишком хорошими:
- Выводилось слишком много нерелевантных результатов
- Включались все товары
- Не было индикации относительно того, какой поисковой терм был найден, результатами поиска были обычные изображения товаров
Чтобы не трогать текущий поиск, была создана новая функция, которая:
- Принуждала искать по фразе вместо поиска по ключевым словам
- Ограничивала поиск по категориям журналов и буклетов
- Выводила на экран и подсвечивала текст, содержащий соответствие поисковой фразе
Первые два требования можно было фактически реализовать с помощью произвольной поисковой формы:
<form role="search" method="get" id="searchform" action="http://www.test.dev/"> <div> <label for="s">Search for:</label> <input type="text" value="" name="s" id="s" /> <input type="hidden" value="1" name="sentence" /> <input type="hidden" value="product" name="post_type" /> <input type="hidden" value="product_cat" name="magazines,books" /> <input type="submit" id="searchsubmit" value="Search" /> </div> </form>
Однако она не смогла бы помочь в случае разметки для поисковых результатов и подсветки поисковых фраз, поэтому был создан новый шаблон страницы, который связывался с отдельной страницей.
Вот основная логика шаблона:
<div id="content" class="col-full"> <section id="main" class="col-left"> <!-- begin search form --> <form action="/library" method="post"> <p> <label for="query"><?php _e( 'Search', 'woothemes' ); ?></label> <input type="text" name="query" id="query" value="<?php echo esc_attr( $query ); ?>" /> </p> <p> <input type="submit" id="searchsubmit" value="<?php _e( 'Search', 'woothemes' ); ?>" /> </p> </form> <!-- end search form --> <?php if( !empty( $query ) ) : $product_cats = 'books, renew, sanctuary'; $args = array( 'posts_per_page' => -1, 'post_type' => 'product', 'orderby' => 'date menu_order', 'order' => 'DESC', 's' => $query, 'sentence' => 1, 'product_cat' => $product_cats, ); // perform the search $posts = new WP_Query( $args ); if( ( $posts->have_posts() ) ) : ?> <header class="page-header"> <h1 class="page-title"><?php printf( __( 'Library Search Results for: %s', 'woothemes' ), $query ); ?></h1> </header> <?php /* The loop */ ?> <ul style="list-style: none"> <?php while ( $posts->have_posts() ): $posts->the_post(); ?> <li style="display: block; margin-bottom: 50px"> <div style="float: left; width: 110px;"> <a href="<?php echo get_permalink(); ?>"> <?php echo get_the_post_thumbnail( $post->ID, array( 175, 175 ) ); ?> </a> </div> <div style="float:left; margin-left: 20px; width: 500px"> <h2 style="margin-top: -10px; padding-top: 0px;"> <a href="<?php echo get_permalink(); ?>" <?php echo apply_highlight( get_the_title() , $query ) ?> </a> </h2> <div><?php echo apply_highlight( get_snippet( get_the_content() , $query ) , $query ) ?></div> </div> <div style="clear:both"></div> </li> <?php endwhile; ?> </ul> <?php wp_reset_postdata(); ?> <?php else : ?> <h1 class="page-title"><?php printf( __( 'Sorry, no matches found for "' . $query .'"', 'woothemes' ) ); ?></h1> <h4>Search Suggestions:</h4> <ul> <li>Check your spelling</li> <li>Try more general words</li> <li>Try different words that mean the same thing</li> </ul> <h1 class="page-title">Or, perhaps these might be of interest...</h1> <?php echo do_shortcode('[product_category per_page="3" columns="3" orderby="date" order="desc" category="books"]'); echo do_shortcode('[product_category per_page="3" columns="3" orderby="date" order="desc" category="renew"]'); echo do_shortcode('[product_category per_page="3" columns="3" orderby="date" order="desc" category="sanctuary"]'); endif; // !(empty ( $posts )) endif; // !(empty ( $query )) ?> </section><!-- /#main --> </div><!-- /#content -->
Как вы можете видеть, форма поиска напоминает стандартную поисковую форму в WordPress, поскольку все манипуляции с вызовом WP_Query выполняются через код, где:
- post_type задается как product
- sentence задается в 1 для инициирования поиска по фразе
- добавляется параметр таксономии для ограничения поиска по трем выбранным категориям товаров
- упорядочивание задается по дате, menu_order – по убыванию
- все записи должны быть возвращены
Важно отметить, что в этом решении отсутствует пагинация. Для произвольного, специфичного поиска пагинация не так нужна, особенно если выполняется поиск по фразе.
Вы можете заметить, что если поисковые результаты будут отсутствовать, то в таком случае на экран будут выведены несколько связанных товаров, чтобы посетитель не покинул страницу.
Вывод результатов требует добавления двух дополнительных функций – одной для получения текста, который содержит поисковую фразу, и другой для подсветки фразы.
function apply_highlight( $the_content , $the_query) { return preg_replace( '/' . $the_query . '/i' , '<span style="background-color: #00FF00">$0</span>' , $the_content ); } function get_snippet( $the_content , $the_query ) { preg_match( '/' . $the_query . '/i' , $the_content , $matches, PREG_OFFSET_CAPTURE ); $snippet = '<ul>'; foreach ($matches as $match): $cutoff = substr( $the_content, 0 , $match[1] ); $start = strripos( $cutoff, '<li>' ); $end = strpos( $the_content, '</li>' , $match[1] ); $snippet .= substr( $the_content, $start, ( $end - $start ) + 4 ); //$snippet .= $match[0] . ' - ' . $match[1]; endforeach; $snippet .= '</ul>'; return $snippet; }
Подсветка фразы реализована с помощью простого регулярного выражения.
Получение текста, содержащего поисковую фразу, оказалось не самым простым действием. Этому сильно поспособствовал тот факт, что контент каждого товара выводится в виде неупорядоченного списка с заголовком и описанием, однако попытка поиска соответствующего регулярного выражения не закончилась ничем, поэтому я решил обратиться к обработке строк.
Результат получился, тем не менее, достаточно хорошим:
Произвольные формы идеальны, когда вы хотите реализовать дополнительный поиск
Произвольные поисковые формы и функции идеальны в том случае, если вы хотите добавить дополнительную поисковую функцию, которая будет работать параллельно основной функции поиска и обладать некоторыми ограничениями.
В большинстве случаев вы сможете реализовать то, что вам нужно, путем использования произвольной поисковой формы, которая передает надлежащие параметры во встроенную функцию поиска. Вы можете легко протестировать использование параметров, добавив их к URL поиска.
Стоит заметить, что написание своей собственной функции поиска – не самая сложная задача. Вы можете воспользоваться дополнительными параметрами, чтобы управлять поведением поиска.
Источник: premium.wpmudev.org/blog