Использование условий OR в meta_query для аргумента query_posts
Я искал везде, но не смог найти ничего подходящего.
Я пытаюсь отфильтровать некоторые результаты, и когда я создаю массив args и передаю его в query_posts, я заметил, что сгенерированный SQL использует AND вместо OR для значения 'class_1_days'. Я хотел бы изменить это на OR. Как это сделать? (Я использую LIKE, так как данные сериализованы, иначе я бы попробовал "IN" и передал массив. Какой правильный способ это сделать?)
$args = array(
'taxonomy' => 'courses',
'term' => 'drawing',
'post_type' => 'school',
'paged' => $paged,
'meta_query' =>
#$checkbox_array,
array(
array(
'key' => 'instructor',
'value' => '1128',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => 'Friday',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => 'Saturday',
'compare' => 'LIKE',
),
)
);
Это соответствующий SQL-запрос, который генерируется, где я заметил AND
[138] => Array
(
[0] => SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (18) ) AND wp_posts.post_type = 'school' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND ( (wp_postmeta.meta_key = 'instructor' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%1128%')
AND (mt1.meta_key = 'class_1_days' AND CAST(mt1.meta_value AS CHAR) LIKE '%Friday%')
AND (mt2.meta_key = 'class_1_days' AND CAST(mt2.meta_value AS CHAR) LIKE '%Saturday%') ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 5

Используйте 'relation' => 'OR'
как в примере из Codex ниже:
$args = array(
'post_type' => 'product',
'meta_query' => array(
'relation' => 'OR', /* <-- здесь */
array(
'key' => 'color',
'value' => 'blue',
'compare' => 'NOT LIKE'
),
array(
'key' => 'price',
'value' => array( 20, 100 ),
'type' => 'numeric',
'compare' => 'BETWEEN'
)
)
);
$query = new WP_Query( $args );
Это обеспечит применение оператора OR
ко всем блокам meta_query. Если вам нужно что-то вроде WHERE "instructor" = 1128 AND (class_1_days = "value1" OR class_1_days = "value2")
, то подойдет что-то подобное, при условии что ваши данные не сериализованы.
$args = array(
'taxonomy' => 'courses',
'term' => 'drawing',
'post_type' => 'school',
'paged' => $paged,
'meta_query' =>
array(
array(
'key' => 'instructor',
'value' => '1128',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => array('Friday','Saturday')
'compare' => 'IN',
)
)
);
Учитывая, что у вас сериализованные данные, это будет сложно реализовать. Вы могли бы написать фильтр для posts_where
, но я ожидаю, что результат будет неэффективным и подверженным ошибкам.
Правильный подход для подобных случаев, когда вам нужно искать/фильтровать/сортировать по определенным данным, на мой взгляд, заключается в том, чтобы изначально не хранить данные в виде сериализованного массива. Это просто неподходящий подход, если вам нужны подобные запросы.
Если бы я взялся за этот проект, я бы перебрал данные, которые нужно запрашивать таким образом, и пересохранил каждую часть как отдельную пару ключ/значение. Части, которые не требуют запросов, могут оставаться сериализованными. Нет (хорошего) способа сделать это в MySQL с помощью функций MySQL, ни как часть запроса, который вы пытаетесь выполнить, ни как отдельный запрос. Я видел попытки создания функции MySQL для "десериализации", но не вижу в этом смысла. Пересохранение данных — правильный путь.
Должен быть довольно простой запрос для получения всех данных class_1_days
. Затем переберите их и сохраните части, по которым нужно выполнять запросы, как независимые ключи, а остальное верните обратно как сериализованные данные.

Спасибо большое за ваш ответ. Можно ли сделать условие AND или OR? Я пытаюсь сохранить условие по преподавателю. Ваш пример делает все условия через OR. Мне нужно, чтобы только условие class_1_days было через OR. По аналогии с тем, как если бы я использовал 'value' => array(bla,bla,bla), 'compare' => IN
По сути, чтобы было: Instructor = 'bla' AND value IN (bla,bla,bla)

Спасибо @S_ha_dam. Есть ли способ десериализовать базу данных? Я унаследовал этот проект, поэтому не знаю, какие последствия это может иметь. Видел ссылку на приложение, которое делает именно это, но ссылка оказалась битой.

Спасибо, но просто пересохранение не поможет — это рабочая среда, где данные постоянно обновляются, и class_1_data — это лишь часть уравнения. Есть множество других — class_0_data, class_2_data и так далее, которые добавляются пользователями. Думаю, лучший подход — откатиться и найти способ переустановить модуль, чтобы данные не сохранялись в сериализованном виде (если я разберусь как) и двигаться дальше X-( ... Я не упоминал другие классы, потому что думал, что смогу разобраться с этим самостоятельно

Вам определенно придется рефакторить функцию "save", но также нужно будет пересохранить существующие данные, иначе получится странная смесь сериализованных и несериализованных данных. Сначала добейтесь работоспособности кода на тестовом сервере, а затем переведите рабочий сервер в режим обслуживания на двадцать минут в период низкой нагрузки.

О да, конечно, мне придется изменить текущие данные!
Это отстой. Я думал, что это будет простое исправление, блин!

Погодите... а что если так... есть ли способ добавить ПОСЛЕ этого фильтра еще один фильтр по преподавателю?
Или даже получить обратно прямой php-массив, который использует WordPress, и отредактировать его?

Я не уверен, что полностью понял последний вопрос, но с помощью фильтров вы можете изменить запрос практически как угодно. Однако, просто "прицепив" что-то, вы вряд ли получите эффективный запрос.

Ну, например, в PHP у вас может быть массив вида classes[1][bla], classes[2][bla] и так далее. Если бы я знал, как называется этот массив, я мог бы просто выполнить поиск по массиву и регулярные выражения на PHP, чтобы отфильтровать результаты самостоятельно после получения нужных данных из OR-оператора, который вы предложили ранее.

Согласно WordPress Codex, вложенные массивы могут использоваться для построения сложных запросов. В вашем примере вам понадобится что-то вроде этого:
$args = array(
'taxonomy' => 'courses',
'term' => 'drawing',
'post_type' => 'school',
'paged' => $paged,
'meta_query' =>
array(
'relation' => 'AND', // отношение по умолчанию
array(
'key' => 'instructor',
'value' => '1128',
'compare' => 'LIKE',
),
array(
'relation' => 'OR',
array(
'key' => 'class_1_days',
'value' => 'Friday',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => 'Saturday',
'compare' => 'LIKE',
),
)
)
);
