WP_Query с «post_title LIKE 'что-то%'»?

44

Мне нужно сделать WP_Queryс собой LIKEна post_title.

Я начал с этого регулярно WP_Query:

$wp_query = new WP_Query( 
    array (
        'post_type'        => 'wp_exposants',
        'posts_per_page'   => '1',
        'post_status'      => 'publish',
        'orderby'          => 'title', 
        'order'            => 'ASC',
        'paged'            => $paged
    )
); 

Но то, что я на самом деле хочу сделать, выглядит так в SQL:

$query = "
        SELECT      *
        FROM        $wpdb->posts
        WHERE       $wpdb->posts.post_title LIKE '$param2%'
        AND         $wpdb->posts.post_type = 'wp_exposants'
        ORDER BY    $wpdb->posts.post_title
";
$wpdb->get_results($query);

Вывод выводит результаты, которые я ожидаю, но я использую регулярное <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>отображение результатов.
И это не работает с $wpdb->get_results().

Как я могу добиться того, что я описал здесь?

лудо
источник

Ответы:

45

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

add_filter( 'posts_where', 'wpse18703_posts_where', 10, 2 );
function wpse18703_posts_where( $where, &$wp_query )
{
    global $wpdb;
    if ( $wpse18703_title = $wp_query->get( 'wpse18703_title' ) ) {
        $where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'' . esc_sql( $wpdb->esc_like( $wpse18703_title ) ) . '%\'';
    }
    return $where;
}

Таким образом, вы все равно можете позвонить WP_Query, просто передайте заголовок в качестве wpse18703_titleаргумента (или измените имя на более короткое).

Ян Фабри
источник
Этот как-то не хватает $wpdb->prepare().
Кайзер
@kaiser: Это было давно, но я думаю, что это было невозможно prepare(). $wpdb->prepare('LIKE "%s%%"', 'banana')будет возвращаться "LIKE ''banana'%'", поэтому мы должны сами построить запрос и выполнить экранирование тоже.
Ян Фабри
1
@JanFabry Рад видеть тебя agaaaaaaaain! :) Заходи в чат, а? StopPress был бы рад видеть вас. Об этом prepare(). Да, это сложно, и я должен был попробовать это несколько раз, прежде чем обойти это. От чего - то я только что сделал: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) ). И я уверен, что esc_sql()это не нужно и просто параноик.
Кайзер
Кажется, что вы не можете найти строку с '(апостроф) внутри. Я думаю, это из-за побега? Я еще не нашел решения
Винсент Деко
19

Упрощенная:

function title_filter( $where, &$wp_query )
{
    global $wpdb;
    if ( $search_term = $wp_query->get( 'search_prod_title' ) ) {
        $where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . esc_sql( like_escape( $search_term ) ) . '%\'';
    }
    return $where;
}

$args = array(
    'post_type' => 'product',
    'posts_per_page' => $page_size,
    'paged' => $page,
    'search_prod_title' => $search_term,
    'post_status' => 'publish',
    'orderby'     => 'title', 
    'order'       => 'ASC'
);

add_filter( 'posts_where', 'title_filter', 10, 2 );
$wp_query = new WP_Query($args);
remove_filter( 'posts_where', 'title_filter', 10, 2 );
return $wp_query;
Рао
источник
13
Пожалуйста, включите объяснение вместе с вашим кодом.
s_ha_dum
2
Большое упрощение
Тимо Хуовинен
1
Код, я думаю, сам объяснил, по крайней мере для меня. Спасибо, что поделились полным сценарием.
Хасан Дад Хан
Используйте '$ wpdb-> esc_like (' вместо 'esc_sql (like_escape ('
fdrv
@fdrv Вы правы, но согласно wp docs $ wpdb-> esc_like по-прежнему требуется esc_sql (). Поэтому я думаю, что правильный код был бы esc_sql ($ wpdb-> esc_like ($ search_term))
Вакас Бухари
16

Хотели обновить этот код, над которым вы работали, для WordPress 4.0 и выше, так как esc_sql () устарела в 4.0 и выше.

function title_filter($where, &$wp_query){
    global $wpdb;

    if($search_term = $wp_query->get( 'search_prod_title' )){
        /*using the esc_like() in here instead of other esc_sql()*/
        $search_term = $wpdb->esc_like($search_term);
        $search_term = ' \'%' . $search_term . '%\'';
        $where .= ' AND ' . $wpdb->posts . '.post_title LIKE '.$search_term;
    }

    return $where;
}

Остальные вещи такие же.

Также я хочу отметить, что вы можете использовать переменную s в аргументах WP_Query для передачи поисковых терминов, которые, как я полагаю, также будут искать заголовок поста.

Так:

$args = array(
    'post_type' => 'post',
    's' => $search_term,
    'post_status' => 'publish',
    'orderby'     => 'title', 
    'order'       => 'ASC'        
);
$wp_query = new WP_Query($args);
Ашан Джей
источник
Что именно search_prod_title? Должен ли я изменить это на что-то еще?
Антониос Цимуртос
С каких это esc_sqlпор он изгнан? Это не. $wpdb->escapeхотя ... developer.wordpress.org/reference/functions/esc_sql
Джереми
Обратите внимание, что параметр s также ищет содержимое сообщения, что может быть нежелательной целью. =)
Кристина Купер
10

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

Сначала мы создаем функцию для posts_whereфильтра, которая позволяет отображать только сообщения, соответствующие определенным условиям:

function cc_post_title_filter($where, &$wp_query) {
    global $wpdb;
    if ( $search_term = $wp_query->get( 'cc_search_post_title' ) ) {
        $where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\'';
    }
    return $where;
}

Теперь мы добавим cc_search_post_titleв наш запрос аргументы:

$args = array(
    'cc_search_post_title' => $search_term, // search post title only
    'post_status' => 'publish',
);

И, наконец, оберните фильтр вокруг запроса:

add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$query = new WP_Query($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );

Использование get_posts ()

Некоторые функции, которые извлекают сообщения, не запускают фильтры, поэтому прикрепленные вами функции фильтра posts_where не будут изменять запрос. Если вы планируете использовать get_posts()для запроса ваших сообщений, вам нужно установить suppress_filtersзначение false в массив аргументов:

$args = array(
    'cc_search_post_title' => $search_term,
    'suppress_filters' => FALSE,
    'post_status' => 'publish',
);

Теперь вы можете использовать get_posts():

add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$posts = get_posts($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );

Как насчет sпараметра?

sПараметр доступен:

$args = array(
    's' => $search_term,
);

При добавлении вашего поискового термина в sпараметр работа, и он будет искать заголовок сообщения, он также будет искать содержимое сообщения.

Как насчет titleпараметра, который был добавлен в WP 4.4?

Передача поискового термина в titleпараметр:

$args = array(
    'title' => $search_term,
);

Чувствителен к регистру и LIKE, нет %LIKE%. Это означает, что поиск helloне вернет сообщение с заголовком Hello Worldили Hello.

Кристина Купер
источник
Превосходно. Я искал post_title в качестве параметра и, очевидно, ничего не нашел.
MastaBaba
7

Основываясь на других ответах до меня, чтобы обеспечить гибкость в ситуации, когда вы хотите найти пост, содержащий слово в метаполе или в заголовке поста, я даю эту опцию через аргумент «title_filter_relation». В этой реализации я допускаю только ввод «ИЛИ» или «И» со значением по умолчанию «И».

function title_filter($where, &$wp_query){
    global $wpdb;
    if($search_term = $wp_query->get( 'title_filter' )){
        $search_term = $wpdb->esc_like($search_term); //instead of esc_sql()
        $search_term = ' \'%' . $search_term . '%\'';
        $title_filter_relation = (strtoupper($wp_query->get( 'title_filter_relation'))=='OR' ? 'OR' : 'AND');
        $where .= ' '.$title_filter_relation.' ' . $wpdb->posts . '.post_title LIKE '.$search_term;
    }
    return $where;
}

Вот пример кода в действии для очень простого типа поста "faq", где вопрос - сам заголовок поста:

add_filter('posts_where','title_filter',10,2);
$s1 = new WP_Query( array(
    'post_type' => 'faq',
    'posts_per_page' => -1,
    'title_filter' => $q,
    'title_filter_relation' => 'OR',
    'post_status' => 'publish',
    'orderby'     => 'title', 
    'order'       => 'ASC',
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'key' => 'faq_answer',
            'value' => $q,
            'compare' => 'LIKE'
        )
    )
));
remove_filter('posts_where','title_filter',10,2);
Дэвид Чой
источник
1
Хорошее понимание, добавление пользовательских «переменных запроса» к передаваемым аргументам запроса WP_Query, чтобы иметь возможность доступа к ним в posts_whereфильтре.
Том Ожер