1

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? :/

  • What is the meta dattime? Your question indicates that the data is stored in a field named event_start. Have you confirmed that your pre_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 circumstances – Tom J Nowell Jul 24 '18 at 2:10
  • ups sorry about the dattime. Slipped in by accident. dattime I used in my sandbox installation and event_start I chose for demonstration purposes here on the site instead. But just forgot to update the code in that direction. Sorry! Did now. – rpk Jul 24 '18 at 9:00
  • The pre_get_postcode runs and the conditional checks are correct imho. Without the is_single() conditional there is DESC instead of ASC sort order for single views. So I would say the pre_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 the event_start custom field. And based on the fact that I am using pre_get_postI suppose the Phlox theme isn't utilizing the main query for the display of posts on the front page :( – rpk Jul 24 '18 at 9:06
  • hmm keep in mind that if you have pagination, you're in an archive, not a singular page, so 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 – Tom J Nowell Jul 24 '18 at 13:05
  • 2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.