Вопрос или проблема
Я пытаюсь настроить “резервный” запрос для wp_query. Я хочу получить первый пост, которому ровно год, но если такого не существует, получить первый пост, которому один год и один день. И я хочу сохранить этот HTML в поле Advanced Custom Field on_this_day_1_year_ago
(для повышения эффективности базы данных).
Первый пример кода работает для первого поста, которому год, и сохраняет HTML в поле on_this_day_1_year_ago
.
Второй пример кода с резервным запросом к посту, которому год и один день, не работает и не выдает никаких ошибок PHP. Что в нем не так?
Я знаю, что мой код со strtotime неряшливый, и вся эта функция требует больших затрат для базы данных.
Это то, с чего я начал; wp_query для поста, которому 1 год, и это работает.
$one_year_ago = date('Y-m-d', strtotime('-1 year'));
$args = array(
'post_type' => 'post',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'date_query' => array(
array(
'year' => date('Y', strtotime($one_year_ago)),
'month' => date('m', strtotime($one_year_ago)),
'day' => date('d', strtotime($one_year_ago)),
),
),
);
$query = new WP_Query( $args );
ob_start();
while( $query->have_posts() ) {
$query->the_post();
echo '<a href="' . get_permalink() '">';
the_title();
echo '</a>';
}
endwhile;
wp_reset_postdata();
$this_day_one_year_ago = ob_get_clean();
// Сохранение в пользовательское поле ACF
update_field('on_this_day_1_year_ago', $this_day_one_year_ago, 'option');
Это объединенный запрос с резервным вариантом к посту, которому один год и один день, который не работает. Есть идеи?
date_default_timezone_set('America/Chicago');
$one_year_ago = date('Y-m-d', strtotime('-1 year'));
$one_year_ago_plus_day = date('Y-m-d', strtotime('-367 days'));
function output_posts( $query ){
// получение вывода для сохранения в поле Advanced Custom Field
ob_start();
while( $query->have_posts() ) {
$query->the_post();
echo '<a href="' . get_permalink() '">';
the_title();
echo '</a>';
}
wp_reset_postdata();
$this_day_one_year_ago = ob_get_clean();
// сохранить в пользовательское поле
update_field('on_this_day_1_year_ago', $this_day_one_year_ago, 'option');
}
// аргументы для 1 года назад
$args = array(
'post_type' => 'post',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'date_query' => array(
array(
'year' => date('Y', strtotime($one_year_ago)),
'month' => date('m', strtotime($one_year_ago)),
'day' => date('d', strtotime($one_year_ago)),
),
),
);
$query = new WP_Query( $args );
// Если в запросе есть посты
if( $query->have_posts() ) {
output_posts( $query );
// или резервный вариант на -367 дней
} else {
// аргументы для 1 года и 1 дня назад
$args = array(
'post_type' => 'post',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'date_query' => array(
array(
'year' => date('Y', strtotime($one_year_ago_plus_day)),
'month' => date('m', strtotime($one_year_ago_plus_day)),
'day' => date('d', strtotime($one_year_ago_plus_day)),
),
),
);
$fallback = new WP_Query( $args );
if ( $fallback->have_posts() ){
output_posts( $fallback );
}
}
PHP strtotime()
принимает некоторые странные строковые значения, которые мы можем использовать в комбинации с ключевым словом ‘before’ в WP_Query date_query. Например, мы можем получить X постов до 1 года назад со следующего дня:
$tomorrow_a_year_ago = wp_date( 'Y-m-d', strtotime( '-1 year +1 day' ) );
$args = array(
'post_type' => 'post',
'posts_per_page' => 5,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'date_query' => array(
array(
'before' => $tomorrow_a_year_ago, // Равен или меньше
),
),
);
$query = new WP_Query( $args );
Пока есть хотя бы 1 пост до (или в) даты год назад, вы получите какой-либо результат. WP_Query создаст следующее SQL условие:
wp_posts.post_date < '2024-01-18 00:00:00'
Так как мы запрашиваем все посты до определенной даты, может случиться так, что следующий пост на самом деле старше года и месяца, или даже старше, если публикация происходит неравномерно в течение года. Следующий кодовый сниппет гарантирует, что следующий найденный пост будет старше, максимум, на 1 год и 7 дней, но не больше.
$days_ago_limit = -7; // 7 дней назад
$post_to_display = null;
foreach( $query->posts as $post ) {
// Создание объекта DateTime на основе разницы между 2 датами.
$dateDiff = date_diff(
date_create( $tomorrow_a_year_ago ), // Объект DateTime
date_create( $post->post_date ) // Объект DateTime
);
// Удаление положительного символа, оставление отрицательного.
$day_diff = (int)ltrim( $dateDiff->format( '%R%a' ), '+' ); // %R = +/- | %a = [0-9]
// Если day_diff находится между сегодня и X дней назад.
if( $day_diff <= 0 && $day_diff >= $days_ago_limit ) {
$post_to_display = $post;
break; // Прерывание цикла
}
}
// У нас есть пост для отображения...
if( $post_to_display ) {
// Делать что-то
}
Лично я бы не сохранял HTML в базе данных, а вместо этого сохранил бы данные. Это обеспечило бы большую гибкость. Допустим, клиент хочет изменить внешний вид вывода, ну, если вы уже сохранили вывод в базе данных, вам придется пересохранять вывод для X постов.
Вместо этого, если мы просто сохраним данные, мы можем позволить плагину/теме/шаблонам структурировать и стилизовать данные по мере необходимости. Я бы рекомендовал просто сохранить ID WP_Post, чтобы если клиент обновил пост по какой-либо причине, мы могли бы получить последние данные.
// У нас есть пост для отображения...
// Функция существует на случай, если нам нужно отключить ACF.
if( function_exists( 'update_field' ) && $post_to_display ) {
update_field( 'on_this_day_1_year_ago', $post_to_display->ID, 'option' );
}
Тогда при выводе:
if( function_exists( 'get_field' ) {
$ago_post_id = get_field( 'on_this_day_1_year_ago', 'option' );
if( ! empty( $ago_post_id ) ) {
// Перезаписываем глобальный $post, чтобы функции шаблона работали как ожидается.
global $post;
$post = get_post( $ago_post_id );
setup_postdata( $post );
printf( '<a href="%s">%s</a>', get_permalink(), get_the_title() );
// Сбрасываем глобальный $post обратно в исходное состояние.
wp_reset_postdata();
}
}
.
Ответ или решение
Разработка резервного решения для пустых результатов wp_query
, основанного на датах публикации постов в WordPress, представляет собой интересную задачу, которая требует тщательной разработки логики для корректного функционирования. Основная цель — это предоставление альтернативного результата, если изначальный запрос не возвращает результатов. В данном случае, если постов, опубликованных ровно год назад, не найдено, необходимо получить посты, опубликованные год и один день назад.
Теория
В основе резервного решения лежит концепция обработки данных и их корректной выборки с учетом временных меток. Функция wp_query
позволяет фокусироваться на различных параметрах, включая типы постов, статус и дату публикации. В данной задаче ключевым элементом является date_query
, который предоставляет возможность фильтрации постов по определенным временным параметрам.
Использование функции date_query
в WordPress позволяет задать точные временные рамки для выборки постов. Однако, если у нас нет записей, соответствующих первой временной метке, необходимо осуществить второй запрос с изменёнными временными рамками, сохраняя принцип минимального изменения данного параметра (один дополнительный день).
Пример
Изначальная часть кода выполняет стандартный запрос для получения постов, опубликованных ровно год назад:
$one_year_ago = date('Y-m-d', strtotime('-1 year'));
$args = array(
'post_type' => 'post',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'date_query' => array(
array(
'year' => date('Y', strtotime($one_year_ago)),
'month' => date('m', strtotime($one_year_ago)),
'day' => date('d', strtotime($one_year_ago)),
),
),
);
$query = new WP_Query($args);
ob_start();
while($query->have_posts()) {
$query->the_post();
echo '<a href="' . get_permalink() . '">';
the_title();
echo '</a>';
}
wp_reset_postdata();
$this_day_one_year_ago = ob_get_clean();
update_field('on_this_day_1_year_ago', $this_day_one_year_ago, 'option');
Однако, если результаты отсутствуют, ваш второй код пытается загрузить посты с новой временной меткой (год и один день назад), но это не работает корректно из-за возможных ошибок в реализации условий и повторного использования wp_query
.
Применение
Для реализации корректной работы скрипта с использованием запроса с резервным решением, сначала убедитесь, что ваш первый запрос работает стабильно. Затем добавьте резервный запрос, если он не возвращает результатов:
date_default_timezone_set('America/Chicago');
$one_year_ago = date('Y-m-d', strtotime('-1 year'));
$one_year_ago_plus_day = date('Y-m-d', strtotime('-367 days'));
// Если первый запрос возвращает результаты
$args = array(
'post_type' => 'post',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'date_query' => array(
array(
'year' => date('Y', strtotime($one_year_ago)),
'month' => date('m', strtotime($one_year_ago)),
'day' => date('d', strtotime($one_year_ago)),
),
),
);
$query = new WP_Query($args);
if ($query->have_posts()) {
output_posts($query);
} else {
// Второй запрос на посты с датой год и один день назад
$args['date_query'] = array(
array(
'year' => date('Y', strtotime($one_year_ago_plus_day)),
'month' => date('m', strtotime($one_year_ago_plus_day)),
'day' => date('d', strtotime($one_year_ago_plus_day)),
)
);
$fallback = new WP_Query($args);
if ($fallback->have_posts()) {
output_posts($fallback);
}
}
Функция output_posts
должна обрабатывать вывод и сохранять данные в Advanced Custom Field (ACF).
Сохранение данных в ACF, а не HTML, обеспечивает гибкость в будущем. Например, если понадобится изменить оформление, данные останутся неизменными — изменятся только шаблоны вывода.
Преимущества гибкого подхода
- Гибкость обновлений: Сохраняя только идентификатор поста, но не HTML, вы обеспечиваете возможность менять способ отображения без необходимости миграции данных.
- Оптимизация производительности: Упрощается логика выборки из базы, так как можно извлечь только необходимые поля.
- Готовность к изменениям: Если необходимо будет изменить стиль или формат вывода, это можно будет сделать, не затрагивая данные.
Итак, следуя указанным шагам, вы развиваете более гибкую и масштабируемую систему для обработки и вывода постов.ələri.