Meta запрос с булевым значением true/false
Я пытаюсь отобразить все объекты недвижимости для аренды, сначала показывая все свободные объекты, а затем все, которые уже арендованы. Есть произвольный тип записи 'rent' с произвольным мета-полем для цены аренды (_price_rented), которое является чекбоксом (возвращает true или false... true, если объект УЖЕ арендован). Мне нужно изменить запрос, чтобы показывать все объекты, причем сначала доступные (не арендованные) объекты, а затем арендованные.
Вот мой запрос:
$ts_properties = new WP_Query(
array(
'post_type' => 'rent',
'paged' => $paged,
'posts_per_page' => -1,
'meta_key' => '_price_rented',
'orderby' => 'meta_value',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => '_price_rented',
'value' => false,
'type' => 'BOOLEAN',
),
)
)
);
По какой-то причине этот запрос показывает все объекты, которые УЖЕ арендованы. Когда я меняю значение с 'false' на 'true' в meta_query, он не показывает никаких объектов.
Затем я подумал, что возвращаемое значение либо false (для объектов, которые арендованы), либо NULL (для объектов, которые НЕ арендованы), но я не уверен, как делать запрос для результата NULL (не false). Я добавил аргумент 'compare' в meta_query и установил значение '!=', но это тоже не сработало.
РЕДАКТИРОВАНИЕ: var_dump возвращает следующее для доступной, не арендованной квартиры: string(0) ""
и для недоступной, арендованной квартиры: string(1) "1"

Кратко: Эта проблема, скорее всего, возникает, когда логическое поле создается как необязательное. Вы можете исправить это, сделав поле обязательным или используя более сложный запрос для получения значений по умолчанию.
Подробности:
Здесь есть две проблемы с представлением данных: первая — какие значения используются для представления true/false, а вторая — сохраняется ли поле вообще, если оно имеет значение по умолчанию (обычно false).
Часть 1: Я изучил SQL, генерируемый WP_Meta_Query
для сравнений с true и false, и обнаружил, что для true он подставляет '1', а для false — '' (пустую строку). Поэтому, если вы выполняете запросы с сравнением фактических значений true и false, данные в базе должны соответствовать этому. В частности, не стоит записывать '0' для false. Возможно, надежнее использовать 0 и 1 (так делают многие конструкторы форм). Но проверьте, что записывается в базу, и учитывайте это при построении запроса.
Часть 2: Предположим, что false — значение по умолчанию. Найти записи со значением true легко:
... 'meta_key' => 'my_key', 'meta_value' => 1
(или true)
Но обратная ситуация сложнее: значение может быть false, а может вообще отсутствовать. Такое бывает, если поле было необязательным в форме — тогда, пока пользователь явно не установит или не изменит его, оно не добавится в базу. Заметим, что если вы используете только get_post_meta
, всё будет работать нормально: возврат false или отсутствие значения дадут одинаковый результат.
Но при использовании WP_Query
всё не так просто. (Или, если есть простой способ, я его пока не нашёл).
У вас есть два (или, возможно, три) варианта:
Убедитесь, что поле всегда явно инициализируется реальным значением. В некоторых конструкторах форм это делается через обязательное поле со значением по умолчанию. Тогда можно проверять
...'meta_value' => 0
.Выполнить два запроса: первый проверяет значение false, второй — отсутствие значения. Их можно объединить в один WP_Query:
meta_query => array( 'relation' => 'OR', array( 'key' => 'my_key', 'value' => 0, 'compare' => '=' ), array( 'key' => 'my_key', 'compare' => 'NOT EXISTS', ), )
Такой запрос, вероятно, не самый эффективный. В зависимости от многих факторов, может быть лучше получить все объекты и отфильтровать их в своём коде.
- Можно использовать "отсутствие значения" как false. Для этого, когда значение должно быть false, нужно удалять мета-значение, а не обновлять его.
В этом случае простой запрос с 'NOT EXISTS'
будет надёжно возвращать правильные объекты. (Я не думаю, что многие конструкторы форм или плагины поддерживают такое поведение, поэтому я бы использовал его только в полностью кастомном коде.)

WP_Meta_Query
— это в некотором роде "не очень стабильная" часть ядра WordPress, и если не быть очень внимательным, она может легко сломаться из-за путаницы.
Когда вы выполняете new WP_Query()
с аргументами meta_query => array()
или их эквивалентами в виде одиночных пар ключ/значение, то в дело вступает new WP_Meta_Query()
, за которым сразу же следует парсинг.
$this->meta_query = new WP_Meta_Query();
$this->meta_query->parse_query_vars( $q );
Допустимые значения
При запросе метаданных есть опция bool
. Если вы её используете, то она вернётся к CHAR
, который является значением по умолчанию, так как массив допустимых значений выглядит так:
'NUMERIC', 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED'
где NUMERIC
будет сброшен в SIGNED
.
Отладка
Существует множество фильтров, которые могут повлиять на процесс сохранения записи, поэтому первое, что нужно сделать — проверить различные значения внутри цикла:
var_dump( get_post_meta( get_the_ID(), '_price_rented', true ) );
Затем, в зависимости от возвращаемого значения, вам либо придётся использовать SIGNED
, если результат 0
или 1
, либо "true"
или "false"
, если результат — строка. Если это действительно булево значение, я всё равно рекомендую использовать string
, чтобы убедиться, что оно пройдёт через $GLOBALS['wpdb']
, который может передавать только строки %s
и цифры %d
.
Дополнительные замечания
Сегодня я обновил запись в Codex для WP_Meta_Query
и увидел, что существует множество различных выводов (добавляющих множество ненужных JOINS
, которые обсуждаются в Trac здесь и здесь с без единичным патчем, перенесённым в ядро). (Последующий тикет для частей AND
здесь). Суть в том, что можно использовать комбинацию аргументов meta_*
вместе с массивом meta_query
и его подмассивами. Результат практически неизвестен, если не сделать дамп, поэтому ИМХО лучше использовать либо один, либо другой способ добавления входных данных. Особенно когда вы используете только meta_key
, так как в некоторых случаях это приводит к "запросу только по ключу".
Решение
Как указано в комментариях:
(...)
var_dump
возвращает следующее для доступной, неарендованной квартиры:string(0) ""
, а для недоступной, арендованной квартиры:string(1) "1"
Теперь meta_query
должен использовать
'meta_query' => array( 'relation' => 'OR', array(
'meta_key' => '_price_rented',
'meta_value' => '1',
'meta_compare' => '='
) );
Если вы хотите получить "недоступные, арендованные квартиры" или используйте '!='
для получения "неарендованных" квартир.
Примечание: Возможные значения для meta_compare
— '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN', 'NOT EXISTS', 'REGEXP', 'NOT REGEXP'
или 'RLIKE'
. Значение по умолчанию — '='
.

Я столкнулся с той же проблемой и после часа поиска обнаружил значения "NOT EXISTS"
и "EXISTS"
(только в WP >= 3.5)
.
Так что нет необходимости запрашивать значение метаполя — достаточно проверить, существует ли meta_key:
'meta_key' => '_price_rented' ,
'meta_compare' => 'NOT EXISTS' ,
Это отлично работает в моём случае.

return false записывается как 0, return true записывается как 1
Поэтому просто добавьте value="1" к вашему input type checkbox, чтобы при отправке формы передавалось значение "1" при отмеченном чекбоксе, а не стандартное "on"
В вашем meta query просто делайте как обычно:
array(
'key' => 'your_key',
'value' => 'your_value',
'compare' => '='
)
