Как создать meta_query с массивом в качестве meta_field?
Вот аргументы для моего запроса:
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports',
)
)
);
Это работает, когда topics
является строкой, но не работает, когда это массив. Я хочу, чтобы этот запрос работал, когда topics
представляет собой, например, array( 'sports', 'nonprofit', etc. )
Существует ли способ создать meta_query с массивами в качестве meta_key?

Передача запросу массива возможных значений
Если значение в базе данных представляет собой строку, и вы хотите передать запросу несколько значений:
$args = array(
'post_type' => 'news', // Тип записи - новости
'meta_query' => array(
array(
'key' => 'topics', // Мета-ключ
'value' => array ( 'sports', 'nonprofit', 'community' ), // Массив значений
'compare' => 'IN' // Оператор сравнения - вхождение в массив
)
)
);
Поиск конкретного значения в сериализованном массиве данных
Если значение в базе данных представляет собой массив нескольких тем, и вы хотите найти одну конкретную тему в этом массиве (обратите внимание, что массив в базе данных может быть получен как массив, но хранится в сериализованном виде, который также является строкой):
$args = array(
'post_type' => 'news', // Тип записи - новости
'meta_query' => array(
array(
'key' => 'topics', // Мета-ключ
'value' => 'sports', // Искомое значение
'compare' => 'LIKE' // Оператор сравнения - поиск подстроки
)
)
);
Использование 'LIKE' в качестве оператора сравнения не является таким однозначным решением, как можно было бы ожидать, но это лучший доступный вариант.
Кроме этого, вашей единственной альтернативой будет получение всех записей, у которых установлен meta_key "topics", и их ручная обработка в цикле, то есть проверка значения внутри цикла и вывод записей при выполнении условия.
ОБНОВЛЕНИЕ 2021
Хотя приведенный выше ответ все еще актуален, ему уже 9 лет.
Также рекомендуем ознакомиться с ответом @sMyle и отличным ответом @Kaji.

Продолжая ответ Йоханнеса, поскольку это сериализованный массив, если вы храните, например, идентификаторы пользователей (как было в моем случае), вам может потребоваться обрабатывать это немного иначе.
Метаданные записи сохранялись в виде:
array( "1", "23", "99");
Да, это числа, но через update_post_meta
они сохранялись как строки.
'meta_query' => array(
array(
'key' => 'my_meta_key',
'value' => serialize( strval( 1 ) ),
'compare' => 'LIKE'
)
)
Фактически вы выполняете сравнение LIKE с сериализованной строковой версией того, что ищете.
ОБНОВЛЕНИЕ 2022:
Следует учитывать одну проблему: если сохраняется НЕ строка (сохраняется как число), и вы используете serialize( intval( $value ) )
, есть риск совпадения с индексом, а не со значением.
Например:
array( 1234, 5678 );
Будет сохранено так:
a:1:{i:0;i:1234;i:1;i:5678;}
Если в meta_query использовать serialize( intval( 1 ) )
, вы получите совпадение с i:1;
, то есть с индексом, а НЕ со значением, поэтому будьте осторожны и, если вы контролируете код, всегда сохраняйте как строку, чтобы не совпадать с индексом.

Случайно наткнулся сегодня на этот старый ответ. Мне нравится ваше дополнение. +1

Я тоже только что с этим столкнулся, моя проблема в том, что мне нужно получить все записи, где user_id не в массиве, но вышеприведенное решение не работает, поэтому я сделал так:
'meta_query' => array(
array(
'key' => 'my_meta_key',
'value' => ':' . $user_id . ';',
'compare' => 'NOT LIKE'
)
)
Потому что при сериализации все значения сохраняются в виде: ':значение;'

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

Еще одно небольшое улучшение ответа от @sMyles.
У меня были случаи, когда ID хранились как в виде строк (например, когда они берутся из поля формы), так и в виде целых чисел (например, update_post_meta($post_id, authorized_users', array(get_current_user_id()));
). Это похоже на известную проблему с wp_set_object_terms()
, где можно использовать ID терминов для их установки, но если не привести их к целым числам заранее, есть примерно 50% шанс создать новые термины с этими числами в качестве их имен.
Это может привести к тому, что они будут храниться совершенно по-разному в сериализованном массиве, как видно из примеров из базы данных моего тестового сайта:
a:1:{i:0;s:1:"1";} // 's' означает 'string' (строка), также обратите внимание на двойные кавычки
a:1:{i:0;i:1;} // 'i' означает 'integer' (целое число), без кавычек
Оба примера выше, если их пропустить через print_r()
, отобразятся как:
Array
(
[0] => 1
)
Чтобы исправить это, я внес небольшую корректировку в meta_query
, добавив relation
и еще одну версию запроса, которая приводит значение к целому числу вместо строки.
Вот окончательный результат:
'meta_query' => array(
'relation' => 'OR', // Указывает, что подходит любой из следующих вариантов
array(
'key' => 'bcm_enm_authorized_users',
'value' => serialize(strval(get_current_user_id())), // Сохранено как строка
'compare' => 'LIKE'
),
array(
'key' => 'bcm_enm_authorized_users',
'value' => serialize(intval(get_current_user_id())), // Сохранено как целое число
'compare' => 'LIKE'
),
),
РЕДАКТИРОВАНИЕ: Только что осознал, что этот метод может привести к коллизиям с индексами массива, что может позволить кому-то получить несанкционированный доступ к материалам, если их нет в массиве, но их ID пользователя совпадает с индексом. Таким образом, хотя этот метод работает при наличии обсуждаемой проблемы, лучшей практикой является приведение всех значений, которые вы хотите искать, к строкам перед сохранением, чтобы можно было использовать метод @sMyles.

Сегодня я столкнулся с похожей проблемой. Мне нужно было выполнить запрос к полю связи ACF (Advanced Custom Fields) с несколькими связанными пользователями (массив).
После обновления поля через PHP запрос не работал. Но когда я обновил его через интерфейс ACF, запрос заработал.
Проблема оказалась в том, что мой PHP-код устанавливал значения связи как целые числа (int), а интерфейс ACF сохранял их как строки (string). Чтобы оба варианта работали, я теперь использую такой запрос (адаптированный под этот пример):
$args = array(
'post_type' => 'news',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'topics',
'value' => '1;', // работает для массива с числами (int)
'compare' => 'LIKE'
),
array(
'key' => 'topics',
'value' => '"1"', // работает для массива со строками (string)
'compare' => 'LIKE'
),
)
);

Я бы выбрал ответ Йоханнеса. Однако хочу его улучшить, потому что при использовании такого meta_query может возникнуть следующая ситуация:
Ваше значение:
array('sports','movies', 'sports2');
Когда вы делаете поиск:
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports',
'compare' => 'LIKE'
)
)
);
Тогда в результатах будут возвращены и 'sport', и 'sport2'.
Чтобы это исправить, измените аргументы meta_query на:
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports";',
'compare' => 'LIKE'
)
)
);
Это потому что значение сериализовано в базе данных, и каждый элемент разделён точкой с запятой. Таким образом, вышеуказанные аргументы сработают.
Если элементы в значении являются числами, вам просто нужно убрать двойные кавычки "
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => '1;',
'compare' => 'LIKE'
)
)
);
