Вопрос или проблема
Запрос должен извлекать данные из тысяч записей на основе двух пользовательских полей. ‘Дата оригинала’ обязательна для каждой записи, а ‘Дата публикации’ является необязательной (скрыта на основе пользовательского флажка. Если отмечена, featured_date по умолчанию устанавливается равной original_date). Однако, если ‘Дата публикации’ установлена для определенной записи, она должна иметь приоритет, и в итоге сортироваться по дате публикации так, как будто дата оригинала была установлена равной дате публикации.
То есть запрос НЕ должен сначала сортировать все записи по дате публикации, а затем по дате оригинала. Вместо этого он должен сортировать их вместе, как если бы эти даты были объединены в один столбец. Таким образом, пользователь может поднять событие на верх результатов, установив дату публикации, но как только создается другая запись с более поздней датой оригинала, запись с датой публикации опускается вниз.
Пример базы данных:
ID | post_title | meta_key | meta_value
---------------------------------------------
1 | Человек 1 | дата_оригинала | 2 Января
2 | Человек 2 | дата_публикации | 1 Января
2 | Человек 2 | дата_оригинала | 1 Декабря
3 | Человек 3 | дата_публикации | 3 Января
Ожидаемые результаты запроса:
ID | post_title | meta_key | meta_value
---------------------------------------------
3 | Человек 3 | дата_публикации | 3 Января
1 | Человек 1 | дата_оригинала | 2 Января
2 | Человек 2 | дата_публикации | 1 Января
И если новая запись с оригинальной (или публикуемой) датой, скажем, 4 Января, будет создана, она теперь окажется на вершине результатов.
Я изучил meta_query и могу получить записи на основе того, существуют ли эти пользовательские поля для данной записи. Но мне не удалось найти способ упорядочить их так, чтобы даты из обоих полей были объединены и упорядочены, а не сначала упорядочивать все по одному полю, а затем по другому.
$args = array(
'post_type' => 'custom_post_type',
'post_status' => 'publish',
'paged' => $paged,
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'featured_date',
'type' => 'DATE',
'compare' => 'EXISTS'
),
array(
'key' => 'original_date',
'type' => 'DATE',
'compare' => 'EXISTS'
)
),
'orderby' => 'meta_value_date',
'order' => 'DESC',
// 'orderby' => array('meta_value_date' => 'DESC')
);
$results = new WP_Query( $args );
Я смог получить желаемые результаты из следующего SQL-запроса (хотя, вероятно, неэффективного, так как я не слишком хорошо разбираюсь в SQL). Однако проблема с использованием этого SQL-запроса заключается в том, что уже есть логика, которая добавляет дополнительные параметры к исходному запросу на основе других факторов. Мне пришлось бы повторно реализовать добавление в SQL-запрос на основе этой логики, чего я хотел бы избежать, найдя правильный способ сделать это с помощью wp_query.
$sql = $wpdb->get_results("SELECT * FROM (
SELECT DISTINCT *, $wpdb->postmeta.meta_value AS sort_date
FROM $wpdb->posts
INNER JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )
WHERE $wpdb->posts.post_type='custom_post_type'
AND $wpdb->postmeta.meta_key = 'featured_date'
AND $wpdb->posts.post_status='publish'
UNION
SELECT DISTINCT *, $wpdb->postmeta.meta_value AS sort_date
FROM $wpdb->posts
INNER JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )
WHERE $wpdb->posts.post_type='custom_post_type'
AND $wpdb->postmeta.meta_key = 'original_date'
AND $wpdb->posts.post_status='publish'
) AS created_table
GROUP BY ID
ORDER BY sort_date DESC");
Правка – ответ birgire заставил меня осознать, что стоит упомянуть, что это для плагина, который уже используется в производстве на многих сайтах. Текущая функциональность сортирует записи по ‘original_date’. Но было запрошено позволить пользователю поднимать определенные записи на вершину, при этом уважая очередь (т. е. более поздние записи будут выше). Дата публикации – это запрашиваемая функциональность.
При вставке/обновлении записи установите menu_order
записи как максимум числовых MMDD
значений поле original_date
и featured_date
. Возможно, установить порядок меню в формате 1MMDD
, чтобы избежать ведущего нуля.
Затем мы можем сортировать записи пользовательского типа по порядку меню очень быстро с помощью:
WP_Query( [
'post_type' => 'custom_post_type',
'orderby' => 'menu_order',
'order' => 'DESC'
] );
где menu_order
– это столбец int(11)
в таблице записей.
Если не все записи пользовательского типа имеют эти пользовательские даты, сгруппируйте их, например, по пользовательской таксономии, чтобы использовать в вашем WP_Query
. Возможно, можно было бы искать записи с ненулевым порядком меню.
Ответ или решение
Для решения задачи сортировки постов в WordPress по двум пользовательским полям ("original_date" и "featured_date") необходимо использовать встроенные возможности класса WP_Query
. Ниже предложены шаги и пример реализации этого решения.
Шаги для реализации
-
Создание и обновление пользовательских полей: Убедитесь, что оба поля задаются корректно, когда посты создаются или обновляются. Поле "featured_date" должно при необходимости принимать значение "original_date".
-
Форматирование даты: Даты в мета-полях должны быть в формате, который позволяет корректное их сравнение (например,
Y-m-d
). -
Сборка аргументов для
WP_Query
: Создайте сложный запрос, который будет учитывать оба поля. Вы можете использоватьmeta_query
для этой цели, а затем объединить результаты. -
Оптимизация сортировки: Для улучшения производительности можно использовать поле
menu_order
, как было предложено, что позволит быстро сортировать посты. При этом необходимо убедиться, что оно обновляется при изменении любой из дат.
Пример кода
Следующий код показывает, как можно реализовать запрос для получения постов, сортированных по дате, используя поля "original_date" и "featured_date":
// Получение значений постов на основе оригинальной и "выбранной" даты
function get_custom_posts() {
global $wpdb;
// Запрос для получения метаданных и сортировки по дате
$query = "
SELECT p.ID, pm.meta_key, pm.meta_value
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_type = 'custom_post_type'
AND p.post_status = 'publish'
AND (pm.meta_key = 'original_date' OR pm.meta_key = 'featured_date')
ORDER BY
CASE
WHEN pm.meta_key = 'featured_date' THEN STR_TO_DATE(pm.meta_value, '%b %d')
ELSE STR_TO_DATE((SELECT pm_inner.meta_value FROM {$wpdb->postmeta} pm_inner WHERE pm_inner.post_id = p.ID AND pm_inner.meta_key = 'original_date'), '%b %d')
END DESC
";
// Выполнение запроса
$results = $wpdb->get_results($query);
return $results;
}
// Использование функции
$posts = get_custom_posts();
Пояснения к коду
-
Запрос: Мы используем SQL-запрос с объединением таблиц
posts
иpostmeta
. Сортировка происходит по выбранной дате или оригинальной дате в соответствии с условиями, указанными вORDER BY
. -
Формат даты: Функция
STR_TO_DATE
используется для преобразования строкового представления даты из поля метаданных в формат, который можно корректно сортировать. -
Поддержка производительности: Такой подход позволяет извлекать данные гибко, избегая необходимости применения тяжелых запросов и объединений.
Заключение
Эта реализация остаётся актуальной и эффективной для использования в продуктивных средах, позволяя динамически сортировать посты по двум связанным мета-полям. Такой метод упрощает запросы к базе данных и повышает производительность, в то время как изменения в логике бизнеса (добавление пользовательского выбора для сортировки) легко интегрируются в текущую архитектуру.
Эти рекомендации помогут вам интегрировать необходимую функциональность сортировки в ваш плагин без значительных изменений, позволяя пользователям видеть более актуальные посты в нужном порядке.