WP_Query с "post_title LIKE 'something%'"?

30 мая 2011 г., 15:11:57
Просмотры: 188K
Голосов: 56

Мне нужно выполнить 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().

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

0
Все ответы на вопрос 6
5
54

Я бы решил это с помощью фильтра для 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 (или измените имя на что-то более короткое).

30 мая 2011 г. 17:06:03
Комментарии

В этом примере почему-то отсутствует $wpdb->prepare().

kaiser kaiser
6 нояб. 2012 г. 17:46:12

@kaiser: Прошло много времени, но, кажется, это было невозможно с prepare(). $wpdb->prepare('LIKE "%s%%"', 'banana') вернул бы "LIKE ''banana'%'", так что нам придется самостоятельно конструировать запрос и выполнять экранирование.

Jan Fabry Jan Fabry
6 нояб. 2012 г. 21:36:02

@JanFabry Рад снова тебя видеть! :) Заходи как-нибудь в чат, а? StopPress будет рад тебя видеть. Насчет этого prepare(). Да, это непросто, мне самому пришлось несколько раз попробовать, прежде чем разобраться. Вот пример из того, что я только что сделал: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) ). И я почти уверен, что esc_sql() здесь излишне и просто параноидально.

kaiser kaiser
7 нояб. 2012 г. 12:35:13

Похоже, что нельзя искать строку с ' (апострофом) внутри. Предполагаю, что это из-за экранирования? Пока не нашёл решение

Vincent Decaux Vincent Decaux
30 авг. 2018 г. 18:10:29

Мне пришлось удалить & из &$wp_query, иначе я получал PHP-ошибку expected to be a reference, value given

ecairol ecairol
28 мая 2021 г. 23:23:27
8
24

Упрощённый вариант:

function title_filter( $where, &$wp_query )
{
    global $wpdb;
    // 2. используем пользовательский запрос:
    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,
    // 1. определяем пользовательскую переменную запроса для передачи поискового запроса:
    '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 );
return $wp_query;
19 апр. 2013 г. 16:24:00
Комментарии

Пожалуйста, включите пояснение вместе с вашим кодом.

s_ha_dum s_ha_dum
19 апр. 2013 г. 16:49:54

Отличное упрощение

Timo Huovinen Timo Huovinen
10 нояб. 2014 г. 21:52:08

Код, я думаю, самодокументирован, по крайней мере для меня. Спасибо за полный скрипт.

Hassan Dad Khan Hassan Dad Khan
8 авг. 2017 г. 09:55:26

Используйте '$wpdb->esc_like(' вместо 'esc_sql( like_escape('

fdrv fdrv
19 февр. 2018 г. 19:16:23

@fdrv Вы правы, но согласно документации WP, $wpdb->esc_like всё ещё требует esc_sql(). Поэтому я считаю, что правильный код будет esc_sql( $wpdb->esc_like( $search_term ) )

Syed Waqas Bukhary Syed Waqas Bukhary
18 окт. 2019 г. 21:31:40

like_escape устарел с версии 4.0.0. Используйте wpdb::esc_like

Empty Brain Empty Brain
14 мар. 2020 г. 08:46:32

remove_filter использует только 3 аргумента, последний можно удалить.

Skatox Skatox
24 июн. 2020 г. 18:03:22

смог заставить вышеуказанное работать, но по какой-то причине при выполнении поиска возвращались данные из других post_types, хотя я указал тип записи в массиве $args. В итоге добавил его в запрос заголовка, что сработало для меня: $where .= ' OR ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\' AND '. $wpdb->posts . '.post_type = \'huguenot-family\'';

philip philip
2 февр. 2021 г. 14:10:44
Показать остальные 3 комментариев
4
21

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

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

    if($search_term = $wp_query->get( 'search_prod_title' )){
        /*используем esc_like() здесь вместо 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);
2 янв. 2015 г. 07:36:51
Комментарии

Что именно представляет собой search_prod_title? Стоит ли изменить это на что-то другое?

Antonios Tsimourtos Antonios Tsimourtos
24 мар. 2017 г. 19:20:18

С каких пор esc_sql устарел? Это не так. $wpdb->escape действительно устарел... https://developer.wordpress.org/reference/functions/esc_sql/

Jeremy Jeremy
2 февр. 2018 г. 19:36:34

Обратите внимание, что параметр s выполняет поиск по содержимому записи, что может не соответствовать желаемой цели. =)

Christine Cooper Christine Cooper
19 апр. 2018 г. 13:43:29

like_escape устарел начиная с версии 4.0.0. Используйте wpdb::esc_like

Empty Brain Empty Brain
14 мар. 2020 г. 08:40:20
3
18

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

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

19 апр. 2018 г. 15:58:35
Комментарии

Отлично. Я искал параметр 'post_title' и, очевидно, ничего не нашел.

MastaBaba MastaBaba
13 сент. 2019 г. 18:55:30

Я получаю ошибку с wp query или get posts: "E_WARNING Error in file »class-wp-hook.php« at line 288: Parameter 2 to cc_post_title_filter() expected to be a reference, value given

Elkrat Elkrat
3 мар. 2020 г. 19:28:10

@Elkrat Убери символ & из &$wp_query в функции cc_post_title_filter.

Mattimator Mattimator
6 мая 2020 г. 00:04:16
2

Развивая предыдущие ответы, чтобы обеспечить гибкость в ситуации, когда нужно найти пост, содержащий слово либо в мета-поле, либо в заголовке поста, я предоставляю такую возможность через аргумент "title_filter_relation". В данной реализации я разрешаю только значения "OR" или "AND" с установленным по умолчанию значением "AND".

function title_filter($where, &$wp_query){
    global $wpdb;
    if($search_term = $wp_query->get( 'title_filter' )){
        $search_term = $wpdb->esc_like($search_term); //вместо 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);
29 апр. 2015 г. 01:44:12
Комментарии

Хорошее наблюдение, добавление пользовательских "query vars" в аргументы запроса, передаваемые в WP_Query, чтобы иметь возможность обращаться к ним внутри фильтра posts_where.

Tom Auger Tom Auger
4 февр. 2017 г. 23:32:55

кажется хорошим ответом, единственная проблема - у меня это не работает :-(

luke_mclachlan luke_mclachlan
28 мар. 2022 г. 14:04:48
0

Оптимальный подход для выполнения WP_Query с поиском 'LIKE' по полю post_title — это включение параметра запроса 'search_columns'. Если вы создаёте WP_Query вручную, его следует указать следующим образом:

'search_columns' => ['post_title']

Подробнее можно узнать в исходном коде WordPress на Trac: https://core.trac.wordpress.org/browser/tags/6.3/src/wp-includes/class-wp-query.php#L1426

Однако, если ваша цель — изменить запросы, сгенерированные сторонними плагинами или темами, вы можете добиться этого с помощью хука 'post_search_columns'.

Официальная документация содержит подробную информацию о том, как использовать этот хук: https://developer.wordpress.org/reference/hooks/post_search_columns/

28 сент. 2023 г. 13:34:38