query_posts - использование meta_compare для сравнения значений мета-полей (больше, меньше или равно)
Я использую query_posts( $args ) для фильтрации цикла Loop.
Мне нужно отфильтровать записи на основе их meta_value "vote", иногда меньше чем, иногда равно и так далее...
Я определенно хочу использовать функцию query_posts() и передавать мой фильтр через $args!
Я не хочу использовать add_filter('posts_where', 'filter_where'); и затем добавлять оператор AND к запросу.
Я хочу использовать встроенную функциональность WordPress для фильтрации записей с помощью meta_key, meta_value и meta_compare таким образом:
$args = array( 'meta_key'=>'vote', 'meta_compare'=>'>=', 'meta_value'=>5, 'posts_per_page'=>100 ) )
query_posts( $args );
Результат этого запроса:
SELECT SQL_CALC_FOUND_ROWS wp_posts.* FROM wp_posts JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) WHERE 1=1 AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND wp_postmeta.meta_key = 'vote' AND wp_postmeta.meta_value >= '5' GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 100
Проблема в следующем:
wp_postmeta.meta_value >= '5'
Должно быть:
wp_postmeta.meta_value >= 5
Тогда это будет работать нормально.
Я не понимаю, почему WordPress добавляет кавычки.
Я использую предопределенные параметры WordPress (<, >, <=, >=) и очевидно, что это будет работать только с числами, а не со строками, которые должны быть в кавычках.
В документации говорится:
Возвращает записи с ключом произвольного поля 'miles' со значением произвольного поля, которое МЕНЬШЕ ИЛИ РАВНО 22
query_posts('meta_key=miles&meta_compare=<=&meta_value=22');
Начиная с WP 3.1, вы можете привести значение метаполя к любому типу, используя аргумент 'type' в 'meta_query':
$args = array(
'meta_query'=> array(
array(
'key' => 'vote', // Ключ метаполя
'compare' => '>=', // Оператор сравнения
'value' => 5, // Значение для сравнения
'type' => 'numeric', // Тип значения (числовой)
)
)
'posts_per_page' => 100 // Количество записей на странице
) );
query_posts( $args ); // Выполнение запроса
При беглом просмотре документации, кажется, что meta_value предназначен для строк, а для числовых значений есть meta_value_num.
Смотрите Параметры Orderby
Обновление
Провёл небольшое исследование.
meta_value_num действительно игнорируется при фильтрации. Думаю, они просто забыли добавить эту часть. :)
Проблема в том, что WP_Query корректно получает число как int (передача в виде массива не имеет значения), но он передаёт сгенерированное условие meta_compare через $wpdb->prepare() и явно помечает значение как строку %s. В этом случае prepare принудительно обрамляет его в одинарные кавычки.
Похоже, вам всё же придётся фильтровать posts_where. Можно попробовать просто убрать кавычки у этой конкретной строки вместо того, чтобы генерировать условие вручную.
Я рекомендую сначала преобразовать ваш массив $args в строку перед передачей в query_posts. Когда вы создаете массив $args, система автоматически преобразует число 5 в строку "5" при преобразовании массива обратно в строку.
Поэтому лучше использовать такую запись:
query_posts('meta_key=vote&meta_compare=>=&meta_value=5&posts_per_page=100');
Таким образом, в query_posts передается та же информация, но число 5 будет обрабатываться как число, а не как строка "5".
Обновление
Как выяснилось, meta_value хранит значения в виде строк, а не чисел, что делает сравнение "больше/меньше" неэффективным. Однако после дополнительного исследования я обнаружил флаг запроса meta_value_num.
Если выполнить следующий вызов query_posts:
query_posts('meta_key=vote&meta_compare=>=&meta_value=5&posts_per_page=100&orderby=meta_value_num');
То вы получите желаемое поведение. meta_value_num указывает WordPress интерпретировать значения meta_value как числа, а не строки.
Я пробовал, это дает точно такой же результат. На самом деле, я даже проследил путь от функции к функции в WordPress-Core, и строка разбивается на & и преобразуется в массив перед обработкой....
User
Согласно Codex: Обратите внимание, что значение 99 будет считаться больше 100, так как данные хранятся в виде строк, а не чисел. Таким образом, meta_value действительно хранится как строка... поэтому WordPress добавляет кавычки. Следующий вопрос: можете ли вы описать, в чем именно "не работает"? Возвращает ли он вообще какие-либо данные?
EAMann
Да, вы правы! Как я уже говорил выше, что бы я ни делал, значение оборачивается в '5'. MySQL не может сравнивать что-то на числовом уровне, если оно обернуто в '', поэтому мне интересно, почему WordPress реализовал эту функцию, если она не работает. Нет, он возвращает результат, но не тот, который ожидается.
User
Смотрите мое обновление выше. Добавление orderby=meta_value_num должно решить проблему, если у вас WP 2.8.4 или новее.
EAMann
Спасибо за все усилия. Но это тоже не сработало. Я действительно глубоко изучил эту тему, и это просто не поддерживается в WP. Я создам тикет по этой проблеме, потому что ее действительно легко исправить, и это откроет столько возможностей!
User
@Joakim Да, это поддерживается WP. Прочтите тикет, на который я ссылался выше, где была добавлена эта функция. Возможно, вы можете предоставить нам более крупный пример кода того, что вы пытаетесь сделать... возможно, проблема в другом.
EAMann
WordPress обрабатывает все 'value' как строки и добавляет к ним одинарные кавычки в итоговом запросе, поэтому SQL также вынужден обрабатывать их как строки, а не числа. Вы можете убрать эти кавычки с помощью фильтра:
add_filter('get_meta_sql', function($data) {
$regex = "/'(-?\d+)'/";
$data['where'] = preg_replace($regex, "$1", $data['where']);
return $data;
});
И убедитесь, что у вас указан 'type' => 'NUMERIC' или аналогичный тип