Display more recent and less recent posts in same category

Is there a way to display not just the-most-recent posts in a category, but the 3 posts in a category more recent (time-wise) than the current post?

For example, Jan10post Jan15post Jan17post | Jan22post (currently viewing) | Jan23post Jan30post Feb2post

I have the following code as a starting point, but how to tell WordPress to start at the current post, find the child-most category of the post, and display less/more recent posts I haven’t found anywhere.

function my_custom_loop_2 ($category, $tag, $offset) {
        $args=array(
          // showposts has been replaced, use 'posts_per_page' instead
          // 'showposts'        =>1,
          'posts_per_page'      => 3,
          // this has been replaced, use 'ignore_sticky_posts'
          // 'caller_get_posts' => 1,
          'ignore_sticky_posts' => true,
        );

        if ($category) {
          $args['cat'] = $category;
        }

        if ($tag) {
          $args['tag'] = $tag;
        }

        if ($offset) {
          $args['offset'] = $offset;
        }

        $my_query = new WP_Query($args);
        // ... rest of function to output loop 

if( $my_query->have_posts() ) {
            while ($my_query->have_posts()) : $my_query->the_post(); ?>
             <div class="three-titles">
              <a href="https://wordpress.stackexchange.com/questions/218112/<?php the_permalink() ?>" ><?php the_title(); ?></a></br>
             </div>
            <?php
            endwhile;
        } //if ($my_query)

        wp_reset_query();  // Restore global post data stomped by the_post(). 

};
and

<?php my_custom_loop_3(13, NULL, 1); ?>

1 Answer
1

The challenge:

Get n = quantity older and newer posts from the recent posts with the same categories and/or tags.

The solution:

Like @TwerkingN00b suggested, we do need some custom SQL here. The magic SQL phrase we are looking for is UNION.

UNION is used to combine the result from multiple SELECT statements into a single result set.

All the other stuff is knowledge how WordPress handles SQL queries, how to get the relevant data and then putting it all together. Let us start:

Setting up the basics. Our reference post (= current ID if nothing is supported), its date and some empty variables additionally we want to direct our query to ignore or accept the associated categories or terms.

function the_assoc_posts($post_id = 0, $quantity = 3, $ignore_categories = false, $ignore_tags = false) {
    if (empty($post_id)) $post_id = get_the_ID();
    if (empty($post_id)) return;

    $post_id = get_the_ID();
    $date = get_the_date( 'Y-m-d H:i:s', $post_id );
    $cat = array();
    $tag = array();

Now we need to get all the categories of our post.

    if (!$ignore_categories) {
        $post_categories = wp_get_post_terms( $post_id, 'category' );
        if (!empty($post_categories) && is_array($post_categories)) {
            foreach ($post_categories as $category) {
                $cat[] =  $category->term_id;
            }
            $cat = implode(',',$cat);
        }
    }

Now we need to get all the post_tags of our post.

    if (!$ignore_tags) {
        $post_tags = wp_get_post_terms( $post_id );
        if (!empty($post_tags) && is_array($post_tags)) {
            foreach ($post_tags as $post_tag) {
                $tag[] =  $post_tag->term_id;
            }
            $tag = implode(',', $tag);
        }
    }

Because WordPress doesn’t supports this kind of queries (yet) we have to rebuild the basic query and adapt it to our needs.

    global $wpdb, $post;

    $innerjoin = array();
    $where = array();

    if (!empty($cat)) {
        $innerjoin[]= "INNER JOIN $wpdb->term_relationships AS tt1 ON ($wpdb->posts.ID = tt1.object_id)";
        $where[] = "tt1.term_taxonomy_id IN ($cat)";
    }
    if (!empty($tag)) {
        $innerjoin[]= "INNER JOIN $wpdb->term_relationships AS tt2 ON ($wpdb->posts.ID = tt2.object_id)";
        $where[] = "tt2.term_taxonomy_id IN ($tag)";
    }
    $innerjoin = implode("\n", $innerjoin);
    if (!empty($where))
        $where="WHERE (".implode(" AND \n", $where).') AND';
    else
        $where = "WHERE";

    $query = "SELECT
      $wpdb->posts.ID,
      $wpdb->posts.post_date as DateOrder,
      2 SortOrder
    FROM $wpdb->posts
    {$innerjoin}
    {$where} $wpdb->posts.ID = {$post_id}
    UNION ALL
    (SELECT
      $wpdb->posts.ID,
      $wpdb->posts.post_date,
      3
    FROM $wpdb->posts
    {$innerjoin}
    {$where}
    $wpdb->posts.post_type="post"
    AND $wpdb->posts.post_status="publish"
    AND $wpdb->posts.post_date > '{$date}'
    GROUP BY $wpdb->posts.ID
    ORDER BY $wpdb->posts.post_date ASC LIMIT $quantity)
    UNION ALL
    (SELECT
      $wpdb->posts.ID,
      $wpdb->posts.post_date,
      1
    FROM $wpdb->posts
    {$innerjoin}
    {$where} $wpdb->posts.post_type="post"
    AND $wpdb->posts.post_status="publish"
    AND $wpdb->posts.post_date < '{$date}'
    GROUP BY $wpdb->posts.ID
    ORDER BY $wpdb->posts.post_date DESC LIMIT $quantity)
    ORDER BY SortOrder ASC, DateOrder DESC";

I found the basics on how to get rows before and after a particular row on stackoverflow and the sorting was also explained there. To find out how the current version of WordPress (v4.4) handles the query I built a custom query with tax_query parameters and echoed the SQL.

Now we need to loop through the found posts.

    $loop = $wpdb->get_results($query);

    if ( $loop ) {
        foreach ( $loop as $row ) {
            $post = get_post($row->ID);
            setup_postdata( $post );
            ?>
            <div class="three-titles">
                <a href="https://wordpress.stackexchange.com/questions/218112/<?php the_permalink() ?>"<?php echo (get_the_ID() == $post_id?' class="current"':'')?>><?php the_title(); ?></a></br>
            </div>
            <?php
        }
    }
    wp_reset_postdata();
}

And we got results. To get the associated posts:

  • Inside the loop: the_assoc_posts();
  • Outside the loop: the_assoc_posts($post_id);
  • With max. of 5 associated posts: the_assoc_posts($post_id, 5);
  • Ignore associated categories: the_assoc_posts(null, 3, false);
  • Ignore associated tags: the_assoc_posts(null, 3, true, false);

Leave a Comment