Создаем свой собственный поиск в WordPress

Дата публикации:Март 14, 2014

Функция поиска в 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, причем выдаваемые записи должны будут содержать поисковую фразу либо в своем заголовке, либо в контенте.

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

Если же вы хотите обновить дефолтную поисковую форму – чтобы запустить поиск по фразам, к примеру – то в таком случае у вас есть два варианта, как поступить; в данном случае мы будем предполагать, что в вашем шаблоне поисковая форма не является жестко кодированной:

  1. Первый вариант – поместить произвольную поисковую форму в шаблон searchform.php. Всякий раз, когда функция get_search_form() будет вызвана, она в первую очередь будет использовать данный шаблон.
  2. Второй вариант – использовать фильтр 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;
}

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

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

Результат получился, тем не менее, достаточно хорошим:

custom_search

Произвольные формы идеальны, когда вы хотите реализовать дополнительный поиск

Произвольные поисковые формы и функции идеальны в том случае, если вы хотите добавить дополнительную поисковую функцию, которая будет работать параллельно основной функции поиска и обладать некоторыми ограничениями.

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

Стоит заметить, что написание своей собственной функции поиска – не самая сложная задача. Вы можете воспользоваться дополнительными параметрами, чтобы управлять поведением поиска.

Источник: premium.wpmudev.org/blog

Поделиться

11 комментариев

  1. вопрос: &exact=1 — у вас работает?

    У меня никак не получилось заставить работать этот вызов. Хотя в файле вордпресса query.php есть вызов $n = !empty($q[‘exact’]) ? » : ‘%’; -так что должно работать, а не хочет. Спрошу еще и у источника

    А &sentence=1 -да это крутая штука. И она работает

  2. Второй комментарий можно удалить. Рега с фейсбука прошла. Но не сообщили что отправили данные реги на почту. Просто тупо перезагрузили страницу и вновь предложили регаться через фейсбук и вордпресс ком.

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

    • Дмитрий Алёшин says:

      Да, тоже замечал это. Никаких уведомлений не присылают, просто перезагружают страницу, я в итоге забил на оставление комментариев. Про exact, к сожалению, не в курсе — этот параметр не задокументирован в кодексе, но имеется в query.php, как вы и привели выше, поэтому, в принципе, должен работать.

  3. Anton says:

    ужасный баннер

  4. Катя says:

    У меня поиск на сайте ограничивается поиском по заголовкам и словам в текстах. Как организовать код, чтобы поиск велся по всему: категориям, меткам, заголовкам?

  5. Дмитрий says:

    Прекрасная замена стандартного поиска — https://wordpress.org/plugins/search-filter/. Умеет искать по разным таксономиям и т.д.

  6. Как раз пытаюсь написать свой поиск по сайту на вордпресс. У меня поиск должен работать не по постам а по таблице с данными кирпичей — она создана с помощью отдельного самописного поиска.
    Ваш материал по поиску мне очень помог.
    Спасибо

  7. Добрый день , что мне нужно сделать, чтобы получить «Поиск точного совпадения», я нашел файл query.php, но так и не понял что делать в нем. Либо не в нем? Помогите пожалуйста. Мне необходимо сделать поиск по меткам так чтобы искало , например «большая футболка», а не «большая» и «футболка»

  8. Не получилось заставить работать этот код. Поэтому в форме я просто добавил еще выпадающий список, в котором пользователь сам может выбрать, по какому полю искать, а потом этот параметр гетом передаю функции.

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.