Вопрос или проблема
Я пытаюсь отобразить все арендные объекты, сначала все объекты, которые не были арендованы, а затем все объекты, которые в настоящее время арендованы. Есть пользовательский тип поста ‘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', ), )
Это, вероятно, неэффективный запрос. В зависимости от множества факторов может быть лучше вернуть все объекты и отфильтровать их в вашем коде.
- Можно использовать ‘no value’ для обозначения false. Для этого, когда значение должно быть установлено как false, вам нужно удалить мета-значение, вместо обновления его.
В этом случае единственный 'NOT EXISTS'
запрос надежно вернет правильные объекты. (Я не думаю, что многие создатели форм или плагины поддерживают это поведение, поэтому я бы использовал это только в чисто пользовательском коде.)
WP_Meta_Query
является чем-то вроде “не очень стабильной” части ядра, и если вы не обращаете на это ВНУТРЕННЕГО ВНИМАНИЯ, он может легко сломаться от путаницы.
Когда вы делаете 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
и его подмассивами. Результат, как правило, неизвестен, если вы его не выведете, поэтому, IMHO, лучше использовать либо один или другой способ добавления данных. Особенно, когда вы только используете 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' ,
Это работает идеально для меня.
возвращение false записывается как 0, возвращение true записывается как 1
Итак, просто добавьте value=”1″ к вашему input типа checkbox, чтобы он мог отправить значение “1”, если отмечено, а не “on” по умолчанию
В вашем мета-запросе просто делайте, как обычно :
array(
'key' => 'your_key',
'value' => 'your_value',
'compare' => '='
)
Ответ или решение
Для решения вашей задачи с отображением арендных свойств в WordPress с использованием пользовательского типа записи и метаполей необходимо более детально разобраться в ваших метаданных и в том, как правильно сформулировать запрос. Проблема заключается в том, что ваши метаполя, связанные с состоянием аренды ("арендовано" и "не арендовано"), неправильно интерпретируются, что и приводит к неожиданным результатам запроса.
1. Препятствия и некорректные значения:
Каждое значение метаполя имеет свое представление в базе данных. Как вы упомянули, для арендованных свойств (‘rented’) метаполе _price_rented
устанавливается на '1'
, тогда как для неарендованных значение просто отсутствует (или равно пустой строке ""
). При этом важно понимать, что в WP_Meta_Query при запросах используется обширный набор операторов сравнения, и поведение может варьироваться в зависимости от заданного условия.
2. Корректная структура запроса:
На основании вашего вопроса и представленных данных, можно сделать вывод, что вам требуется следующий запрос для получения всех арендных свойств с приоритетом на неарендованные:
$ts_properties = new WP_Query(
array(
'post_type' => 'rent',
'paged' => $paged,
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_price_rented',
'compare' => 'NOT EXISTS' // для получения неарендованных
),
array(
'key' => '_price_rented',
'value' => '0', // или пустая строка
'compare' => '='
)
)
)
);
3. Сортировка и порядок:
Чтобы отсортировать полученные данные так, чтобы неарендованные свойства шли по первому, а арендованные – по последнему, необходимо модифицировать ваш запрос, используя возможности orderby
. Сначала вы должны использовать meta_value
для сортировки:
$ts_properties = new WP_Query(
array(
'post_type' => 'rent',
'paged' => $paged,
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_price_rented',
'compare' => 'NOT EXISTS' // для неарендованных
),
array(
'key' => '_price_rented',
'value' => '1', // для арендованных свойств
'compare' => '='
)
),
'orderby' => array(
'meta_value' => 'ASC', // сортировка по возрастанию
),
'meta_key' => '_price_rented', // мета ключ для сортировки
)
);
4. Дополнительные рекомендации:
-
Проверка значений метаполей: Для лучшего понимания того, что действительно хранится в базе данных, используйте
var_dump(get_post_meta(get_the_ID(), '_price_rented', true));
для отладки и проверки значений. -
Обработка пустых значений: Если у вас есть метаполя, которые могут быть не заданы, используйте
compare => 'NOT EXISTS'
в вашемmeta_query
, чтобы захватить эти записи. -
Построение индексов: Если в вашей базе данных много записей, думайте о возможной оптимизации через индексы для метаполей.
Используя эти подходы, вы сможете построить корректный запрос, который будет возвращать все необходимые записи, сортируя их по статусу аренды.