Как создать meta_query с массивом в качестве meta_field?

15 июн. 2012 г., 16:04:59
Просмотры: 85.6K
Голосов: 31

Вот аргументы для моего запроса:

$args = array(
    'post_type' => 'news',
    'meta_query' => array(
        array(
            'key' => 'topics',
            'value' => 'sports',
        )
    )
);

Это работает, когда topics является строкой, но не работает, когда это массив. Я хочу, чтобы этот запрос работал, когда topics представляет собой, например, array( 'sports', 'nonprofit', etc. )

Существует ли способ создать meta_query с массивами в качестве meta_key?

2
Комментарии

Пожалуйста, уточните — вы имеете в виду, что сохранённое значение "topics" является массивом? Или что сохранённое значение — это строка, и вы хотите передать несколько терминов в запрос в виде массива?

MathSmath MathSmath
15 июн. 2012 г. 17:03:29

@MathSmath, я имею в виду, что сохранённое значение является массивом.

mike23 mike23
15 июн. 2012 г. 17:08:13
Все ответы на вопрос 5
0
58

Передача запросу массива возможных значений

Если значение в базе данных представляет собой строку, и вы хотите передать запросу несколько значений:

$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.

15 июн. 2012 г. 17:27:20
5
28

Продолжая ответ Йоханнеса, поскольку это сериализованный массив, если вы храните, например, идентификаторы пользователей (как было в моем случае), вам может потребоваться обрабатывать это немного иначе.

Метаданные записи сохранялись в виде:

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;, то есть с индексом, а НЕ со значением, поэтому будьте осторожны и, если вы контролируете код, всегда сохраняйте как строку, чтобы не совпадать с индексом.

6 июн. 2015 г. 06:09:33
Комментарии

serialize(strval(1)) решил мою проблему, спасибо

Behzad Behzad
10 янв. 2016 г. 22:45:58

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

Johannes Pille Johannes Pille
20 окт. 2016 г. 09:07:04

Я тоже только что с этим столкнулся, моя проблема в том, что мне нужно получить все записи, где user_id не в массиве, но вышеприведенное решение не работает, поэтому я сделал так:

'meta_query' => array( array( 'key' => 'my_meta_key', 'value' => ':' . $user_id . ';', 'compare' => 'NOT LIKE' ) )

Потому что при сериализации все значения сохраняются в виде: ':значение;'

Bobz Bobz
19 мар. 2018 г. 06:29:15

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

Johannes Pille Johannes Pille
16 авг. 2020 г. 11:30:35

Вау, это спасло мой день!

Akshay K Nair Akshay K Nair
17 мая 2022 г. 09:20:31
2
10

Еще одно небольшое улучшение ответа от @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.

13 июл. 2018 г. 05:44:28
Комментарии

Это должен быть выбранный ответ, самый надежный

Amin Abdolrezapoor Amin Abdolrezapoor
23 окт. 2019 г. 19:26:15

Да, вы абсолютно правы (в вашем редактировании), и представьте себе, я случайно сделал это в своей собственной кодовой базе, сохраняя как целое число вместо строки, поэтому всегда сохраняйте как строку (добавил правку в своем ответе тоже)

sMyles sMyles
25 февр. 2022 г. 00:07:01
0

Сегодня я столкнулся с похожей проблемой. Мне нужно было выполнить запрос к полю связи 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'
        ),
    )
);
6 июл. 2018 г. 09:22:29
0

Я бы выбрал ответ Йоханнеса. Однако хочу его улучшить, потому что при использовании такого 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'
            )
        )
    );
2 нояб. 2016 г. 10:22:00