I have the following task I'm trying to accomplish. I want to display events in the post
post type. I want to sort them by date and time and there should be events only displayed happening right now or in the future. Old posts shouldn't be displayed in the front-end at all. Therefore I have attached a custom date time field event_start
with Advanced Custom Fields 5.6.10
to the post type post
. For test purposes I have only seven posts, six upcoming and one passed with the following dates.
Post 0
Publish date: 28.06.2018
Event date: 05.07.2018 18:00
Post 1
Publish date: 30.05.2018
Event date: 06.08.2018 21:08
Post 2
Publish date: 29.05.2018
Event date: 06.08.2018 21:20
Post 3
Publish date: 14.07.2018
Event date: 10.08.2018 08:00
Post 4
Publish date: 23.07.2018
Event date: 12.08.2018 08:00
Post 5
Publish date: 24.07.2018
Event date: 23.08.2018 08:00
In the WordPress backend I've set the number of shown posts to 4. The website is using the Phlox theme which is based on Elementor. For narrowing down the posts and satisfying the described constraints I've created a small plugin utilising pre_get_posts
. The particular function created looks like this:
function mv_pre_get_posts( $query ) {
if ( class_exists( 'ACF' ) ) {
// Queries aren't modified for the admin menu.
if ( is_admin() ) {
return $query;
}
// Queries are modified for the post type 'post' only.
if ( isset( $query->query_vars['post_type'] ) && ( 'post' === $query->query_vars['post_type'] || is_single() ) ) {
$query->set( 'orderby', 'meta_value' );
$query->set( 'meta_key', 'event_start' );
$query->set( 'order', 'ASC' );
$query->set( 'posts_per_page', get_option( 'posts_per_page' ) );
$query->set( 'meta_query', array(
'relation' => 'AND',
array(
'key' => 'event_start',
'compare' => '>=',
'value' => date( 'Y-m-d H:i:s', strtotime( '-8 hours' ) ),
'type' => 'DATETIME',
),
) );
}
return $query;
} else {
add_action( 'admin_notices', 'mv_mvquery_notice' );
}
}
add_action( 'pre_get_posts', 'mv_pre_get_posts' );
I've also set the posts_per_page
as recommended here How to fix pagination for custom loops? and used the get_option
function to pull the set number automatically. If I take a look at the front-page now, Post 1
to Post 4
are displayed correctly on the first page. But now the issues with pagination are about to start. If I go to the second page again Post 1
to Post 4
are shown, instead of Page 5
only, which I would have expected.
If I click onto Page 4
to get into the single view Post 5
is correctly shown as next as well as Post 3
for previous. If I go on clicking the previous links further I get to Post 0
, which shouldn't show up at all, then Post 1
then Post 2
.
If I start from Post 1
in single view, instead of only seeing the next Post 2
link I get Post 2
for previous and Post 0
for next. After clicking Post 0
next, Post 3
, Post 4
and Post 5
follow up.
Is there a way to make the pagination adhere to the settings for pre_get_post
and minding the given query results for the front-page as well as for each single view with the correct sorting order and number of displayed posts?
Update
I've fixed the issue with the front-page pagination by setting the paged attribute. The function looks the following way now, but the issue that Post 0
shows up when displaying single posts, and also mixing up the sort order still persists. :/
function mv_pre_get_posts( $query ) {
if ( class_exists( 'ACF' ) ) {
// Queries aren't modified for the admin menu.
if ( is_admin() ) {
return $query;
}
// Queries are modified for the post type 'post' only.
if ( isset( $query->query_vars['post_type'] ) && ( 'post' === $query->query_vars['post_type'] || is_single() ) ) {
$query->set( 'orderby', 'meta_value' );
$query->set( 'meta_key', 'event_start' );
$query->set( 'order', 'ASC' );
$query->set( 'paged', get_query_var( 'paged' ) );
$query->set( 'posts_per_page', get_option( 'posts_per_page' ) );
$query->set( 'meta_query', array(
'relation' => 'AND',
array(
'key' => 'event_start',
'compare' => '>=',
'value' => date( 'Y-m-d H:i:s', strtotime( '-8 hours' ) ),
'type' => 'DATETIME',
),
) );
}
return $query;
} else {
add_action( 'admin_notices', 'mv_mvquery_notice' );
}
}
add_action( 'pre_get_posts', 'mv_pre_get_posts' );
Update 2
After a lot of fiddling I managed to create custom queries with the get_adjacent_post
filters. Basically the posts get properly sorted now and Post 0
isn't shown anymore:
function mv_adjacent_post_join() {
global $wpdb;
return "INNER JOIN $wpdb->postmeta AS mvpm ON mvpm.post_id = p.ID";
}
add_filter( 'get_previous_post_join', 'mv_adjacent_post_join' );
add_filter( 'get_next_post_join', 'mv_adjacent_post_join' );
function mv_previous_post_where() {
global $wpdb, $post;
$mv_event_start = date( 'Y-m-d H:i:s', strtotime( '-8 hours' ) );
return $wpdb->prepare( " WHERE mvpm.meta_key = 'event_start' AND CAST(mvpm.meta_value AS DATETIME) >= %s AND p.post_type = 'post' AND p.post_status = 'publish'", $mv_event_start );
}
add_filter( 'get_previous_post_where', 'mv_previous_post_where' );
function mv_next_post_where() {
global $wpdb, $post;
$mv_event_start = date( 'Y-m-d H:i:s', strtotime( '-8 hours' ) );
return $wpdb->prepare(" WHERE mvpm.meta_key = 'event_start' AND CAST(mvpm.meta_value AS DATETIME) >= %s AND p.post_type = 'post' AND p.post_status = 'publish'", $mv_event_start );
}
add_filter( 'get_next_post_where', 'mv_next_post_where' );
function mv_previous_post_sort() {
return "ORDER BY mvpm.meta_value DESC LIMIT 1";
}
add_filter( 'get_previous_post_sort', 'mv_previous_post_sort' );
function mv_next_post_sort() {
return "ORDER BY mvpm.meta_value ASC LIMIT 1";
}
add_filter( 'get_next_post_sort', 'mv_next_post_sort' );
The only thing I was unable to accomplish and figure out is how to compare the current post to the next and previous. In the link-template.php
it is accomplished by comparing p.post_date
with $post->post_date
. But aren't those basically the same? But in my case I would have to compare the event_start
field of the current post to the previous and next. mvpm.meta_value > mvpm.meta_value
and mvpm.meta_value < mvpm.meta_value
doesn't make much sense inside the SQL of the WHERE filters? :/
dattime
? Your question indicates that the data is stored in a field namedevent_start
. Have you confirmed that yourpre_get_posts
code runs and that your conditional checks are correct? I would also note that your fix for custom loops is unnecessary because you are not using custom loops. You're instead modifying the main query, and so the fix is not necessary and may actually cause unrelated issues in some circumstancespre_get_post
code runs and the conditional checks are correct imho. Without theis_single()
conditional there isDESC
instead ofASC
sort order for single views. So I would say thepre_get_post
code is correct. The pagination is the issue. And i suppose I don't use the main query. If i check Query Monitor I notice there is the main query and afterwards the query containing theevent_start
custom field. And based on the fact that I am usingpre_get_post
I suppose the Phlox theme isn't utilizing the main query for the display of posts on the front page :(is_single
doesn't make sense. And yes if your theme is replacing the main query with its own then that complicate things immensely, however, I'm not so sure that what you're trying to do and what you want are quite the same, you talk about this as if it's pagination in an archive, but it is not. It's the next and previous post functionality, which is very different