WP Query Args - Заголовок или значение мета-поля
Как сделать запрос по 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 ...)

Обратите внимание, что часть 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;
});
}
});

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

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

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

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

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

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

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

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

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

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

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

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

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

Я не смог найти готового решения для поиска по нескольким ключевым словам, которые могут находиться в заголовке записи, описании И/ИЛИ в одном или нескольких мета-полях, поэтому создал собственное дополнение к функции поиска.
Всё, что нужно - добавить следующий код в 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'.
Ключевые слова могут находиться в одном или нескольких указанных полях, в любом порядке - будут возвращены все записи, содержащие все ключевые слова в любом из указанных полей.
Вы можете, конечно, заставить поиск всегда работать с определённым списком мета-полей, включённых в функцию, и избавиться от дополнительных аргументов, если хотите, чтобы ВСЕ поисковые запросы включали эти мета-поля :)
Надеюсь, это поможет тем, кто столкнулся с той же проблемой, что и я!

Я довольно новичок в 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);
Я уверен, что это можно улучшить! Надеюсь, это поможет!
