Поиск, который будет искать в произвольном поле, заголовке записи и содержимом записи
Я ищу способ выполнить поиск, который будет проверять произвольное поле с названием 'keywords', заголовок записи и содержимое записи. Если в любом из этих полей найдутся результаты, соответствующие поисковому запросу пользователя, они должны отображаться на странице результатов для пользовательского типа записей (programs).
Мне не нужна помощь с конечным отображением результатов (оно уже работает так, как мне нужно), но мне нужно сделать так, чтобы при поиске проверялись все три поля (заголовок записи, содержимое записи и произвольное поле keywords), и если в любом из них есть результат, соответствующий поисковому запросу, он должен отображаться. Вот код, который у меня есть на данный момент. Сейчас он ищет только в произвольном поле keywords:
elseif($program_search) {
// поиск по тексту программы
query_posts(array(
'post_type' => 'program',
'meta_query' => array(
array(
'key' => 'keywords',
'value' => $program_search,
'compare' => 'LIKE'
),
)
));
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
$l.= "<div class='program-item'>";
$l.= "<div class='program-item-image'><a href='".get_permalink($post->ID)."'>". get_the_post_thumbnail($post->ID, 'thumbnail')."</a></div>";
$l.= "<div class='program-item-title'><a href='".get_permalink($post->ID)."'>".get_the_title($post->ID)."</a></div>";
$l.= "<div class='program-item-content'>".get_the_excerpt()."</div>";
$l.= "<div style='clear:both;'></div>";
$l.= "</div>";
endwhile;
else:
endif;
}

Во-первых, не используйте query_posts
.
Во-вторых, вы можете передать параметр s
, чтобы достичь большей части желаемого.
$program_search = 'test';
$args = array(
'post_type' => 'program',
's' => $program_search,
'meta_query' => array(
array(
'key' => 'keywords',
'value' => $program_search,
'compare' => 'LIKE'
),
)
);
$t = new WP_Query($args);
var_dump($t->request);
Параметр s
активирует обычные механизмы поиска, и поиск выполняется по заголовку и содержимому. Если посмотреть на сгенерированный запрос, можно увидеть...
SELECT
SQL_CALC_FOUND_ROWS
wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
WHERE 1=1
AND (((wp_posts.post_title LIKE '%test%') OR (wp_posts.post_content LIKE '%test%')))
AND (wp_posts.post_password = '')
AND wp_posts.post_type = 'program'
AND (wp_posts.post_status = 'publish')
AND ((wp_postmeta.meta_key = 'keywords' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%test%'))
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 5
Это большая часть того, что вам нужно. LIMIT
, если не указано иное, соответствует значению, установленному в wp-admin→Настройки→Общие. Однако есть проблема.
AND ((wp_postmeta.meta_key = 'keywords' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%test%'))
Я почти уверен, что вам нужно, чтобы это было OR ((wp_postmeta.meta_key ...
, и вы действительно хотите объединить это с post_title
и post_content
. Что-то вроде этого:
AND (
(
(wp_posts.post_title LIKE '%test%')
OR
(wp_posts.post_content LIKE '%test%')
OR
(wp_postmeta.meta_key = 'keywords' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%test%')
)
)
WP_Query
не делает этого, поэтому нам придется реализовать это с помощью фильтров. Доказательство концепции:
function add_join_wpse_99849($joins) {
global $wpdb;
return $joins . " INNER JOIN {$wpdb->postmeta} ON ({$wpdb->posts}.ID = {$wpdb->postmeta}.post_id)";
}
function alter_search_wpse_99849($search,$qry) {
global $wpdb;
$add = $wpdb->prepare("({$wpdb->postmeta}.meta_key = 'keywords' AND CAST({$wpdb->postmeta}.meta_value AS CHAR) LIKE '%%%s%%')",$qry->get('s'));
$pat = '|\(\((.+)\)\)|';
$search = preg_replace($pat,'(($1 OR '.$add.'))',$search);
return $search;
}
$program_search = 'test';
$args = array(
'post_type' => 'program',
's' => $program_search
);
add_filter('posts_join','add_join_wpse_99849');
add_filter('posts_search','alter_search_wpse_99849',1,2);
$t = new WP_Query($args);
remove_filter('posts_join','add_join_wpse_99849');
remove_filter('posts_search','alter_search_wpse_99849',1,2);
// выводим некоторые данные
var_dump($t->request);
var_dump($t->posts);
Обратите внимание, что я полностью исключил meta_query
и в значительной степени продублировал функциональность. Это сделано для того, чтобы избежать генерации проблемного AND
.
Вы добавляете и сразу же удаляете эти фильтры, чтобы они не мешали другим запросам. Есть и другие способы убрать фильтры из других запросов. Один из таких методов описан здесь. Вы также можете добавить remove_filter
в колбэк add_filter
, чтобы они автоматически удалялись.
