WP Query Args - Заголовок или значение мета-поля

18 февр. 2015 г., 02:59:25
Просмотры: 22.2K
Голосов: 12

Как сделать запрос по meta_value или заголовку?

Если я устанавливаю meta_values,

$args['meta_query'] = array(
   'relation' => 'OR',
    array(
        'key' => 'model_name', // название_модели
        'value' => $thesearch, // поисковый_запрос
        'compare' => 'like' // сравнение
    )
);

они автоматически добавляются как AND.

Этот поиск будет:

WHERE title = 'Search' AND (model_name = 'Search' OR ...)

Мне нужно:

WHERE title = 'Search' OR (model_name = 'Search' OR ...)
1
Комментарии

Я попробовал добавить $args['relation'] = 'OR'; Но это не распознается. Похоже, я не понимаю, как управлять условной логикой через args.

Bryan Bryan
18 февр. 2015 г. 03:04:49
Все ответы на вопрос 3
16
28

Обратите внимание, что часть relation в аргументе meta_query используется только для определения связи между подзапросами метаданных.

Вы можете попробовать такую настройку:

$args = [ 
    '_meta_or_title' => $thesearch,   // Наш новый пользовательский аргумент!
    'meta_query'    => [
        [
            'key'     => 'model_name',
            'value'   => $thesearch,
            'compare' => 'like'
        ]
    ],
];

где мы ввели пользовательский аргумент _meta_or_title для активации запроса метаданные ИЛИ заголовок.

Это поддерживается следующим плагином:

<?php
/**
 *  Plugin Name:   Запрос Meta OR Title в WP_Query
 *  Description:   Активируется через аргумент '_meta_or_title' в WP_Query 
 *  Plugin URI:    http://wordpress.stackexchange.com/a/178492/26350
 *  Plugin Author: Birgir Erlendsson (birgire)
 *  Version:       0.0.1
 */

add_action( 'pre_get_posts', function( $q )
{
    if( $title = $q->get( '_meta_or_title' ) )
    {
        add_filter( 'get_meta_sql', function( $sql ) use ( $title )
        {
            global $wpdb;

            // Выполняется только один раз:
            static $nr = 0; 
            if( 0 != $nr++ ) return $sql;

            // Модифицируем часть WHERE:
            $sql['where'] = sprintf(
                " AND ( %s OR %s ) ",
                $wpdb->prepare( "{$wpdb->posts}.post_title = '%s'", $title ),
                mb_substr( $sql['where'], 5, mb_strlen( $sql['where'] ) )
            );
            return $sql;
        });
    }
});
18 февр. 2015 г. 07:12:24
Комментарии

Вау. Потрясающий ответ! Это должно быть в ядре.

Bryan Bryan
18 февр. 2015 г. 22:06:29

Ответ помог мне наполовину. http://wordpress.stackexchange.com/questions/178574/acf-relationship-field-search-filtering Есть идеи по поводу следующего шага?

Bryan Bryan
18 февр. 2015 г. 22:16:36

Я использовал этот ответ для создания запроса по одному конкретному мета-значению на странице edit.php для пользовательского типа записи. Я вообще не запрашиваю заголовок, так как мой пользовательский тип записи не имеет заголовка. Поэтому я заменил " AND ( %s OR %s ) " на " OR ( %s ) " в SQL-запросе и полностью убрал строку $wpdb->prepare( "{$wpdb->posts}.post_title = '%s'", $title ),. Правильно ли это решение?

BdN3504 BdN3504
17 мая 2015 г. 13:30:57

@BdN3504 Рад слышать, что вы нашли это частично полезным. Если я правильно вас понял, ваши модификации должны работать.

birgire birgire
17 мая 2015 г. 16:20:48

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

BdN3504 BdN3504
17 мая 2015 г. 22:32:03

@BdN3504 ок, отлично, я не знаю нативного способа (без использования фильтров) изменить AND -> OR для всего meta-запроса, но вы, конечно, можете модифицировать отношения meta-запроса разными способами внутри meta_query в WP_Query.

birgire birgire
17 мая 2015 г. 22:40:43

Отличный трюк. Если вам нужен поиск подстроки в заголовке, замените post_title = '%s' на post_title like '%%%s%%'.

jarnoan jarnoan
30 сент. 2015 г. 13:11:47

Как выполнить запрос с заголовком и значением таксономии вместо значения метаполя?

Jeff Jeff
13 окт. 2018 г. 18:11:23

@jeff Я как-то написал плагин для объединения запросов: https://github.com/birgire/wp-combine-queries. Также здесь я экспериментировал с поиском (заголовок, анонс, содержание) или таксономическим запросом. Здесь я нашел еще один похожий ответ, с которым экспериментировал. Мы также можем написать два отдельных WP_Query, затем собрать и объединить полученные ID записей в третий WP_Query (если это необходимо и если PHP-сортировки недостаточно) через параметр post__in.

birgire birgire
13 окт. 2018 г. 20:39:24

@birgire Я выбрал путь с двумя отдельными запросами, и это сработало, спасибо.

Jeff Jeff
13 окт. 2018 г. 22:39:17

@Jeff Рад слышать, что это сработало для вас.

birgire birgire
13 окт. 2018 г. 22:42:18

@biergie Спасибо за это потрясающее решение! Оно отлично работает, но мой поиск не возвращает результатов, когда я использую _meta_or_title + meta query + tax query. При использовании вашего пользовательского фильтра и добавлении tax_query элемент $sql['where'] оказывается пустым. У вас есть идеи, как это исправить?

chris.ribal chris.ribal
6 июн. 2019 г. 15:50:42

@biergie Спасибо. Я давно не тестировал это, но вы можете убедиться, что нет других плагинов/кода, которые на это влияют, и, например, запустить с включенной отладкой.

birgire birgire
7 июн. 2019 г. 13:05:50

@birgire - просто интересно, не могли бы вы или кто-то указать, где вы используете $args? спасибо.

v3nt v3nt
29 июн. 2020 г. 19:48:55

$q->get( '_meta_or_title' ) это то же самое, что и $args['_meta_or_title'].

birgire birgire
30 июн. 2020 г. 15:01:01

Великолепное решение! Я бы предложил добавить комментарий jarnoan о использовании LIKE вместо =, так как это очень полезный, но часто упускаемый из виду совет

Kai Qing Kai Qing
22 окт. 2020 г. 05:53:00
Показать остальные 11 комментариев
1

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

Всё, что нужно - добавить следующий код в function.php, и когда вы используете аргумент 's' в WP_Query() и хотите, чтобы поиск осуществлялся также в одном или нескольких мета-полях, просто добавьте аргумент 's_meta_keys', который является массивом ключей мета-полей, в которых нужно искать:

/************************************************************************\
|**                                                                    **|
|**  Позволяет функции поиска WP_Query() искать по нескольким ключам   **|
|**  в мета-полях в дополнение к post_title и post_content             **|
|**                                                                    **|
|**  Автор: rAthus @ Arkanite                                          **|
|**  Создано: 2020-08-18                                               **|
|**  Обновлено: 2020-08-19                                             **|
|**                                                                    **|
|**  Просто используйте обычный аргумент 's' и добавьте 's_meta_keys'  **|
|**  содержащий массив ключей мета-полей для поиска :)                 **|
|**                                                                    **|
|**  Пример:                                                           **|
|**                                                                    **|
|**  $args = array(                                                    **|
|**      'numberposts'  => -1,                                         **|
|**      'post_type' => 'post',                                        **|
|**      's' => $MY_SEARCH_STRING,                                     **|
|**      's_meta_keys' => array('META_KEY_1','META_KEY_2');            **|
|**      'orderby' => 'date',                                          **|
|**      'order'   => 'DESC',                                          **|
|**  );                                                                **|
|**  $posts = new WP_Query($args);                                     **|
|**                                                                    **|
\************************************************************************/
add_action('pre_get_posts', 'my_search_query'); // добавляем специальную функцию поиска к каждому запросу get_posts (включая WP_Query())
function my_search_query($query) {
    if ($query->is_search() and $query->query_vars and $query->query_vars['s'] and $query->query_vars['s_meta_keys']) { // если используется поиск с аргументом 's' и добавлен аргумент 's_meta_keys'
        global $wpdb;
        $search = $query->query_vars['s']; // получаем строку поиска
        $ids = array(); // инициализируем массив ID записей для каждого ключевого слова
        foreach (explode(' ',$search) as $term) { // разбиваем ключевые слова и ищем совпадения для каждого
            $term = trim($term); // удаляем лишние пробелы
            if (!empty($term)) { // проверяем, что ключевое слово не пустое
                $query_posts = $wpdb->prepare("SELECT * FROM {$wpdb->posts} WHERE post_status='publish' AND ((post_title LIKE '%%%s%%') OR (post_content LIKE '%%%s%%'))", $term, $term); // ищем в заголовке и содержимом как обычная функция
                $ids_posts = [];
                $results = $wpdb->get_results($query_posts);
                if ($wpdb->last_error)
                    die($wpdb->last_error);
                foreach ($results as $result)
                    $ids_posts[] = $result->ID; // собираем ID найденных записей
                $query_meta = [];
                foreach($query->query_vars['s_meta_keys'] as $meta_key) // формируем запрос для поиска в каждом указанном мета-поле
                    $query_meta[] = $wpdb->prepare("meta_key='%s' AND meta_value LIKE '%%%s%%'", $meta_key, $term);
                $query_metas = $wpdb->prepare("SELECT * FROM {$wpdb->postmeta} WHERE ((".implode(') OR (',$query_meta)."))");
                $ids_metas = [];
                $results = $wpdb->get_results($query_metas);
                if ($wpdb->last_error)
                    die($wpdb->last_error);
                foreach ($results as $result)
                    $ids_metas[] = $result->post_id; // собираем ID записей из мета-полей
                $merged = array_merge($ids_posts,$ids_metas); // объединяем ID из заголовков, содержимого и мета-полей
                $unique = array_unique($merged); // удаляем дубликаты
                if (!$unique)
                    $unique = array(0); // если нет результатов, добавляем ID "0", иначе будут возвращены все записи
                $ids[] = $unique; // добавляем массив найденных ID в основной массив
            }
        }
        if (count($ids)>1)
            $intersected = call_user_func_array('array_intersect',$ids); // если несколько ключевых слов, оставляем только ID, найденные для всех ключевых слов
        else
            $intersected = $ids[0]; // иначе оставляем единственный массив ID
        $unique = array_unique($intersected); // удаляем дубликаты
        if (!$unique)
            $unique = array(0); // если нет результатов, добавляем ID "0", иначе будут возвращены все записи
        unset($query->query_vars['s']); // удаляем обычный поисковый запрос
        $query->set('post__in',$unique); // добавляем фильтр по ID записей
    }
}

Пример использования:

$search= "ключевые слова для поиска";

$args = array(
    'numberposts'   => -1,
    'post_type' => 'post',
    's' => $search,
    's_meta_keys' => array('short_desc','tags');
    'orderby' => 'date',
    'order'   => 'DESC',
);

$posts = new WP_Query($args);

Этот пример будет искать ключевые слова "ключевые слова для поиска" в заголовках записей, описаниях и мета-полях 'short_desc' и 'tags'.

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

Вы можете, конечно, заставить поиск всегда работать с определённым списком мета-полей, включённых в функцию, и избавиться от дополнительных аргументов, если хотите, чтобы ВСЕ поисковые запросы включали эти мета-поля :)

Надеюсь, это поможет тем, кто столкнулся с той же проблемой, что и я!

18 авг. 2020 г. 21:01:43
Комментарии

Это выглядит довольно надежно. Можешь помочь с добавлением пользовательских полей автора записи [user]?

Adeerlike Adeerlike
30 мар. 2022 г. 15:39:08
1

Я довольно новичок в WP и не слишком много тестировал этот подход, который придумал. Может быть, вы сможете помочь мне проверить, правильный ли он. Решение, которое я нашел, заключается в реализации той же логики meta_query, но с некоторыми заменами.

Сначала пример использования:

$args = array(
    'lang' => 'pt', // эта функция не конфликтует (например, с polylang)
    'post_type' => 'produtos', 
    'post_status'   => 'publish',
    'posts_per_page' => 10,
    'paged' =>  1,
    'fields' => 'ids',
);
$args['meta_query] = [
    ['relation'] => 'OR'; // любое отношение, которое вам нужно
    [
        'key' => 'acf_field', // любое произвольное поле (обычное использование)
        'value' => $search, // любое значение (обычное использование)
        'compare' => 'LIKE', // любое сравнение (обычное использование)
    ],
    [
        'key' => 'post_title', // установите стандартное содержимое WordPress, которое вам нужно ('post_content', 'post_title' и 'post_excerpt')
        'value' => $search, // любое значение
        'compare' => 'LIKE', // тестировалось с 'LIKE' и '=', работает отлично, и я не вижу других потребностей.
    ],
    [
        'key' => 'post_exerpt', // можно добавить сколько угодно раз
        'value' => $search_2,
        'compare' => 'LIKE', 
    ],
];
$the_query = new WP_Query( $args ); // просто запрос
wp_reset_postdata(); // очистка запроса

Для работы добавьте эту функцию в functions.php вашей темы:

function post_content_to_meta_queries($where, $wp_query){
    global $wpdb;

    // если нет metaquery, до свидания!
    $meta_queries = $wp_query->get( 'meta_query' );
    if( !$meta_queries || $meta_queries == '' ) return $where;

    // если только одно отношение
    $where = str_replace($wpdb->postmeta . ".meta_key = 'post_title' AND " . $wpdb->postmeta . ".meta_value", $wpdb->posts . ".post_title", $where);
    $where = str_replace($wpdb->postmeta . ".meta_key = 'post_content' AND " . $wpdb->postmeta . ".meta_value", $wpdb->posts . ".post_content", $where);
    $where = str_replace($wpdb->postmeta . ".meta_key = 'post_excerpt' AND " . $wpdb->postmeta . ".meta_value", $wpdb->posts . ".post_excerpt", $where);

    //// для вложенных отношений

    // подсчет количества meta queries для возможных замен
    $number_of_relations = count($meta_queries);

    // замена 'WHERE' с использованием многомерной логики именования postmeta, используемой в WordPress
    $i = 1;
    while($i<=$number_of_relations && $number_of_relations > 0){
        $where = str_replace("mt".$i.".meta_key = 'post_title' AND mt".$i.".meta_value", $wpdb->posts . ".post_title", $where);
        $where = str_replace("mt".$i.".meta_key = 'post_content' AND mt".$i.".meta_value", $wpdb->posts . ".post_content", $where);
        $where = str_replace("mt".$i.".meta_key = 'post_excerpt' AND mt".$i.".meta_value", $wpdb->posts . ".post_excerpt", $where);
        $i++;
    }

    return $where;
}

add_filter('posts_where','post_content_to_meta_queries',10,2);

Я уверен, что это можно улучшить! Надеюсь, это поможет!

30 июл. 2021 г. 22:43:56
Комментарии

Обновление: я использую это решение уже полтора года. Настоящее спасение для сложных поисков вместе с ACF.

Jeff Oliva Jeff Oliva
19 дек. 2022 г. 15:04:36