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

17 мая 2013 г., 23:18:59
Просмотры: 23.6K
Голосов: 4

Я ищу способ выполнить поиск, который будет проверять произвольное поле с названием '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;
}
0
Все ответы на вопрос 1
1
14

Во-первых, не используйте 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, чтобы они автоматически удалялись.

18 мая 2013 г. 01:19:04
Комментарии

Можете объяснить, почему не стоит использовать query_posts?

Lee Lee
12 окт. 2018 г. 13:07:54