WP_Query: проблема с meta_query relation OR и orderby meta_value

5 мар. 2014 г., 19:06:05
Просмотры: 16.7K
Голосов: 3

Я уже несколько часов бьюсь над этой ошибкой и не могу найти решение. Возможно ли, что в WordPress есть баг с типом отношения 'OR'?

$evenements = new WP_Query(array(
    'post_type'     => 'evenements',
    'orderby'       => 'meta_value',
    'meta_key'      => 'startDate',
    'order'         => 'ASC',
    'meta_query'    => array(
        'relation'  => 'OR',
        array(
            'key'       => 'startDate',
            'value'     => date('Ymd'),
            'compare'   => '>=',
        ),
        array(
            'key'       => 'endDate',
            'value'     => date('Ymd'),
            'compare'   => '>=',
        ),
    ),
));

Что странно: если я ставлю relation 'AND', всё работает как задумано. Если оба условия истинны, пост появляется. Если же ставлю relation 'OR', показываются все посты, и по какой-то причине сортировка тоже перестаёт работать. Даже смена 'order' с 'ASC' на 'DESC' не меняет порядок результатов.

Чтобы понять запрос, вот детали того, что я пытаюсь сделать:

Мне нужно показать события, которые идут сейчас или будут в будущем. Два метаполя - это дата начала события (startDate) и дата окончания (endDate).

То есть если дата начала больше или равна (чтобы включить события на сегодня) сегодняшней дате ИЛИ дата окончания больше или равна сегодняшней дате, показывать пост.

Вторая часть (с endDate) нужна, чтобы отображались события, которые уже начались. А первую часть (с startDate) нельзя убирать, потому что если событие длится один день, клиент заполнит только метаполе startDate.

Я исключил баг в плагинах, так как отключил все плагины, а проблема осталась.

Вот что я получаю при echo $evenements->request:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
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)
JOIN wp_icl_translations t
  ON wp_posts.ID = t.element_id AND t.element_type = 'post_evenements'
JOIN wp_icl_languages l ON t.language_code=l.code AND l.active=1
WHERE 1=1
AND wp_posts.post_type = 'evenements'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
AND (
 wp_postmeta.meta_key = 'startDate'
 OR ( mt1.meta_key = 'startDate' AND CAST(mt1.meta_value AS CHAR) >= '20140305' )
 OR ( mt2.meta_key = 'endDate' AND CAST(mt2.meta_value AS CHAR) >= '20140305' )
)
AND t.language_code='fr'
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value ASC
LIMIT 0, 10
2
Комментарии

Что вы получите, если выполните echo $evenements->request;?

gmazzap gmazzap
5 мар. 2014 г. 20:43:13

Добавлено в тело вопроса.

Fredy31 Fredy31
5 мар. 2014 г. 20:56:59
Все ответы на вопрос 1
1

Проблема в том, что WordPress получает все записи, у которых есть мета-ключ 'startDate', независимо от значения этого ключа.

Это видно из этой части запроса:

...
AND (
 wp_postmeta.meta_key = 'startDate'
 OR ( mt1.meta_key = 'startDate' AND CAST(mt1.meta_value AS CHAR) >= '20140305' )
 OR ( mt2.meta_key = 'endDate' AND CAST(mt2.meta_value AS CHAR) >= '20140305' )
)
...

Таким образом, если у записи есть мета-ключ 'startDate', она будет возвращена.

Причина такого поведения - аргумент 'meta_key':

...
new WP_Query( array(
    'post_type'     => 'evenements',
    'orderby'       => 'meta_value',
    'meta_key'      => 'startDate', // <-- вот в чем проблема
    ...

Однако, если его убрать, вы не сможете сортировать по мета-значению, потому что иногда WordPress ведет себя... (не знаю английского термина, допишите сами).

Решение (должно быть) в том, чтобы первую часть вашего мета-запроса (касающуюся начальной даты) указать как обычные аргументы (не часть meta_query), а вторую часть (касающуюся конечной даты) поместить внутрь массива meta_query:

$evenements = new WP_Query(array(
  'post_type'      => 'evenements',
  'meta_key'       => 'startDate',
  'meta_value'     => date('Ymd'),
  'meta_type'      => 'NUMERIC',
  'meta_compare'   => '>=',
  'orderby'        => 'meta_value',
  'order'          => 'ASC',
  'meta_query'     => array(
    'relation'  => 'OR',
     array (
       'key'     => 'endDate',
       'value'   => date('Ymd'),
       'compare' => '>=',
       'type'    => 'NUMERIC'
     )
  )
));

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

6 мар. 2014 г. 01:47:16
Комментарии

Это только что спасло меня от огромной головной боли. Огромное спасибо!

Jody Heavener Jody Heavener
10 дек. 2014 г. 23:29:19