PHP strtotime()
takes some weird string values that we can use in combination with the WP_Query date_query ‘before’ keyword. For example, we can get X posts before 1 year before tomorrow:
$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, // Less than or Equal To
),
),
);
$query = new WP_Query( $args );
As long as there’s 1 post before (or on) the year ago date you will get some result. WP_Query will create the following SQL condition:
wp_posts.post_date < '2024-01-18 00:00:00'
Since we are querying all posts before a specific date, there could be a scenario where the next post is actually a year + 1 month old, or older, if post publishing is happening sporadically throughout the year. The below code snippet ensures that the next found post is, at most, 1 year + 7 days old, but never older.
$days_ago_limit = -7; // 7 Days Ago
$post_to_display = null;
foreach( $query->posts as $post ) {
// Create a DateTime Object based on the diff of the 2 dates.
$dateDiff = date_diff(
date_create( $tomorrow_a_year_ago ), // DateTime Object
date_create( $post->post_date ) // DateTime Object
);
// Trim the positive character, keep the negative.
$day_diff = (int)ltrim( $dateDiff->format( '%R%a' ), '+' ); // %R = +/- | %a = [0-9]
// IF day_diff is betweeen today and X days ago.
if( $day_diff <= 0 && $day_diff >= $days_ago_limit ) {
$post_to_display = $post;
break; // Break out of loop
}
}
// We have a post to display...
if( $post_to_display ) {
// Do Things
}
Personally, I would not save the HTML to the database but instead I’d save the data. This will allow greater flexibility. Say the client wants to change how the output looks, well, if you’ve already got the output saved to the database you will need to re-save the output for X posts.
Instead, if we just save the data, we can have the plugin/theme/templates structure and style the data as needed. I’d recommend just saving the WP_Post ID so that if the client updates the Post for whatever reason, we can get the latest data.
// We have a post to display...
// Function exists in case we need to disable ACF.
if( function_exists( 'update_field' ) && $post_to_display ) {
update_field( 'on_this_day_1_year_ago', $post_to_display->ID, 'option' );
}
Then when we output:
if( function_exists( 'get_field' ) {
$ago_post_id = get_field( 'on_this_day_1_year_ago', 'option' );
if( ! empty( $ago_post_id ) ) {
// Overwrite the global $post so template functions work as expected.
global $post;
$post = get_post( $ago_post_id );
setup_postdata( $post );
printf( '<a href="%s">%s</a>', get_permalink(), get_the_title() );
// Reset the global $post back to original.
wp_reset_postdata();
}
}