Get Next / Prev 3 Posts in Relation to Current Post

I have 7 posts, like this:

1
2
3
4 – this is the current post
5
6
7

Read More

As noted, number four is the current post being displayed. I need to create a query that will allow me to display the previous 3 posts (by publication date) and also the three posts after. This can be done with two separate queries.

I’ve been able to display the immediately previous or next post, just not the ones further down / up.

Any ideas?

Related posts

Leave a Reply

4 comments

  1. This can be done in a single query, though i can’t speak specifically about how well this query will perform(i’ve not spent a great deal with Union queries – never had a need, till now)..

    First, a function to select two sets of results, but using union to return them as a single result set.

    function get_post_siblings( $limit = 3, $date = '' ) {
        global $wpdb, $post;
    
        if( empty( $date ) )
            $date = $post->post_date;
    
        //$date = '2009-06-20 12:00:00'; // test data
    
        $limit = absint( $limit );
        if( !$limit )
            return;
    
        $p = $wpdb->get_results( "
        (
            SELECT 
                p1.post_title, 
                p1.post_date,
                p1.ID
            FROM 
                $wpdb->posts p1 
            WHERE 
                p1.post_date < '$date' AND 
                p1.post_type = 'post' AND 
                p1.post_status = 'publish' 
            ORDER by 
                p1.post_date DESC
            LIMIT 
                $limit
        )
        UNION 
        (
            SELECT 
                p2.post_title, 
                p2.post_date,
                p2.ID 
            FROM 
                $wpdb->posts p2 
            WHERE 
                p2.post_date > '$date' AND 
                p2.post_type = 'post' AND 
                p2.post_status = 'publish' 
            ORDER by
                p2.post_date ASC
            LIMIT 
                $limit
        ) 
        ORDER by post_date ASC
        " );
        $i = 0;
        $adjacents = array();
        for( $c = count($p); $i < $c; $i++ )
            if( $i < $limit )
                $adjacents['prev'][] = $p[$i];
            else
                $adjacents['next'][] = $p[$i];
    
        return $adjacents;
    }
    

    There’s a test date in there, you can safely ignore that or add in your own value for testing.

    Here’s some sample code you can use in your single.php loop to list out the results, though note this is just a generic example, and the function might need to select more/different data, but based on the info you’ve provided i wasn’t sure exactly what you wanted, so the following is for illustration and to give a sample you can use to test the results..

    <?php 
    $siblings = get_post_siblings( 3 ); // This is the same as doing the call below(which is just for illustration)
    //$siblings = get_post_siblings( 3, $post->post_date );
    
    $prev = $siblings['prev'];
    
    foreach( $prev as $p )
        echo get_the_time( 'd m Y', $p ) . ': ' . apply_filters( 'the_title', $p->post_title ) . '<br />';
    
    $next = $siblings['next'];
    
    foreach( $next as $p )
        echo get_the_time( 'd m Y', $p ) . ': ' . apply_filters( 'the_title', $p->post_title ) . '<br />';
    ?>
    

    Awaiting feedback… 🙂

  2. This can be done much better with the date_query property of WP_Query class. This will get the posts before the publish date of the current post

    // WP_Query arguments
    $args = array (
        'post_type'              => 'post',
        'post_status'            => 'publish',
         'date_query'    => array(
            'column'  => 'post_date',
            'before'   => get_the_date()
        ),
    );
    
    // The Query
    $the_query = new WP_Query( $args );
    

    ………

  3. 3 x get_adjacent_post():

    global $post;
    $current_post = $post; // remember the current post
    
    for($i = 1; $i <= 3; $i++){
      $post = get_previous_post(); // this uses $post->ID
      setup_postdata($post);
    
      // do your stuff here       
      the_title();
    
    }
    
    $post = $current_post; // restore
    

    same for next 3 posts, just change the function to get_next_post()…


    To do this with a single query, while still using the WP API, try changing the LIMIT value to 3 within the get_previous_post_sort and get_next_post_sort filters.

  4. As JanFabry suggests in an answer to @onetrickpony above, you can modify get_adjacent_post(). This is what I did. Here’s the function. I changed the function signature because it made more sense to me this way.

    /**
     * Retrieve multiple adjacent posts. Adapted from get_adjacent_post()
     *
     * Can either be next or previous post.
     *
     * @since 2.5.0
     *
     * @param int       $post_id    Optional. Will fall back to loop.
     * @param int       $limit      Optional. Number of posts to return.
     * @param bool          $previous       Optional. Whether to retrieve previous or next posts.
     * @param bool          $in_same_term   Optional. Whether post should be in a same taxonomy term.
     * @param array|string  $excluded_terms Optional. Array or comma-separated list of excluded term IDs.
     * @param string        $taxonomy       Optional. Taxonomy, if $in_same_term is true. Default 'category'.
     * @return mixed        Array of post objects if successful. Null if global $post is not set. Empty string if no corresponding post exists.
     */
    function pst_get_adjacent_posts( $post_id = null, $limit = 1, $previous = true, $in_same_term = false, $excluded_terms = '', $taxonomy = 'category' ) {
        global $wpdb;
    
        if ( ( ! $post = get_post( $post_id ) ) || ! taxonomy_exists( $taxonomy ) )
            return null;
    
        $current_post_date = $post->post_date;
    
        $join = '';
        $posts_in_ex_terms_sql = '';
        if ( $in_same_term || ! empty( $excluded_terms ) ) {
            $join = " INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id";
    
            if ( $in_same_term ) {
                if ( ! is_object_in_taxonomy( $post->post_type, $taxonomy ) )
                    return '';
                $term_array = wp_get_object_terms( $post->ID, $taxonomy, array( 'fields' => 'ids' ) );
                if ( ! $term_array || is_wp_error( $term_array ) )
                    return '';
                $join .= $wpdb->prepare( " AND tt.taxonomy = %s AND tt.term_id IN (" . implode( ',', array_map( 'intval', $term_array ) ) . ")", $taxonomy );
            }
    
            $posts_in_ex_terms_sql = $wpdb->prepare( "AND tt.taxonomy = %s", $taxonomy );
            if ( ! empty( $excluded_terms ) ) {
                if ( ! is_array( $excluded_terms ) ) {
                    // back-compat, $excluded_terms used to be $excluded_terms with IDs separated by " and "
                    if ( false !== strpos( $excluded_terms, ' and ' ) ) {
                        _deprecated_argument( __FUNCTION__, '3.3', sprintf( __( 'Use commas instead of %s to separate excluded terms.' ), "'and'" ) );
                        $excluded_terms = explode( ' and ', $excluded_terms );
                    } else {
                        $excluded_terms = explode( ',', $excluded_terms );
                    }
                }
    
                $excluded_terms = array_map( 'intval', $excluded_terms );
    
                if ( ! empty( $term_array ) ) {
                    $excluded_terms = array_diff( $excluded_terms, $term_array );
                    $posts_in_ex_terms_sql = '';
                }
    
                if ( ! empty( $excluded_terms ) ) {
                    $posts_in_ex_terms_sql = $wpdb->prepare( " AND tt.taxonomy = %s AND tt.term_id NOT IN (" . implode( $excluded_terms, ',' ) . ')', $taxonomy );
                }
            }
        }
    
        $adjacent = $previous ? 'previous' : 'next';
        $op = $previous ? '<' : '>';
        $order = $previous ? 'DESC' : 'ASC';
    
        /**
         * Filter the JOIN clause in the SQL for an adjacent post query.
         *
         * The dynamic portion of the hook name, $adjacent, refers to the type
         * of adjacency, 'next' or 'previous'.
         *
         * @since 2.5.0
         *
         * @param string $join           The JOIN clause in the SQL.
         * @param bool   $in_same_term   Whether post should be in a same taxonomy term.
         * @param array  $excluded_terms Array of excluded term IDs.
         */
        $join  = apply_filters( "get_{$adjacent}_post_join", $join, $in_same_term, $excluded_terms );
    
        /**
         * Filter the WHERE clause in the SQL for an adjacent post query.
         *
         * The dynamic portion of the hook name, $adjacent, refers to the type
         * of adjacency, 'next' or 'previous'.
         *
         * @since 2.5.0
         *
         * @param string $where          The WHERE clause in the SQL.
         * @param bool   $in_same_term   Whether post should be in a same taxonomy term.
         * @param array  $excluded_terms Array of excluded term IDs.
         */
        $where = apply_filters( "get_{$adjacent}_post_where", $wpdb->prepare( "WHERE p.post_date $op %s AND p.post_type = %s AND p.post_status = 'publish' $posts_in_ex_terms_sql", $current_post_date, $post->post_type), $in_same_term, $excluded_terms );
    
        /**
         * Filter the ORDER BY clause in the SQL for an adjacent post query.
         *
         * The dynamic portion of the hook name, $adjacent, refers to the type
         * of adjacency, 'next' or 'previous'.
         *
         * @since 2.5.0
         *
         * @param string $order_by The ORDER BY clause in the SQL.
         */
        $sort  = apply_filters( "get_{$adjacent}_post_sort", "ORDER BY p.post_date $order LIMIT $limit" );
    
        $query = "SELECT p.ID FROM $wpdb->posts AS p $join $where $sort";
        $query_key = 'adjacent_post_' . md5( $query );
        $result = wp_cache_get( $query_key, 'counts' );
        if ( false !== $result ) {
            if ( $result )
                $result = array_map( 'get_post', $result );
            return $result;
        }
    
        $result = $wpdb->get_col( $query );
        if ( null === $result )
            $result = '';
    
        wp_cache_set( $query_key, $result, 'counts' );
    
        if ( $result )
            $result = array_map( 'get_post', $result );
    
        return $result;
    }