17

Is it possible to display related posts from same category as the current post?

4 Answers 4

32

One possibility:

$related = get_posts( 
    array( 
        'category__in' => wp_get_post_categories( $post->ID ), 
        'numberposts'  => 5, 
        'post__not_in' => array( $post->ID ) 
    ) 
);

if( $related ) { 
    foreach( $related as $post ) {
        setup_postdata($post);
        /*whatever you want to output*/
    }
    wp_reset_postdata();
}

Reference:

Answer re-written based on WP_Query():

$related = new WP_Query(
    array(
        'category__in'   => wp_get_post_categories( $post->ID ),
        'posts_per_page' => 5,
        'post__not_in'   => array( $post->ID )
    )
);

if( $related->have_posts() ) { 
    while( $related->have_posts() ) { 
        $related->the_post(); 
        /*whatever you want to output*/
    }
    wp_reset_postdata();
}
4
  • 1
    Be very careful with your variable names! You're using a global $post to perform your query, then redefining $post in the same context within your foreach loop.
    – EAMann
    Commented Oct 2, 2011 at 16:15
  • @EAMann - you are generally right - however in my experience, using setup_postdata() with anything other than $post does not deliver the right output for the_title() for instance. wp_reset_postdata() should in theory take care of resetting $post.
    – Michael
    Commented Oct 2, 2011 at 17:08
  • 1
    That's because setup_postdata() itself references $post as a global. Instead of using get_posts() you should define a custom instance of WP_Query and use that to get your posts. It will set up the post data for you as well as make the_title() et al work the way they're supposed ti.
    – EAMann
    Commented Oct 3, 2011 at 1:12
  • 1
    @EAMann This is a rather old post, but it would be appreciated if you could write an answer on how to do that correctly with the custom query and all Commented Nov 25, 2015 at 15:47
2

Here another clean and very flexible option:

Put this code in your functions.php file

function example_cats_related_post() {

    $post_id = get_the_ID();
    $cat_ids = array();
    $categories = get_the_category( $post_id );

    if(!empty($categories) && is_wp_error($categories)):
        foreach ($categories as $category):
            array_push($cat_ids, $category->term_id);
        endforeach;
    endif;

    $current_post_type = get_post_type($post_id);
    $query_args = array( 

        'category__in'   => $cat_ids,
        'post_type'      => $current_post_type,
        'post__not_in'    => array($post_id),
        'posts_per_page'  => '3'


     );

    $related_cats_post = new WP_Query( $query_args );

    if($related_cats_post->have_posts()):
         while($related_cats_post->have_posts()): $related_cats_post->the_post(); ?>
            <ul>
                <li>
                    <a href="<?php the_permalink(); ?>">
                        <?php the_title(); ?>
                    </a>
                    <?php the_content(); ?>
                </li>
            </ul>
        <?php endwhile;

        // Restore original Post Data
        wp_reset_postdata();
     endif;

}

Now you can simply call the function anywhere in your site using:

<?php example_cats_related_post() ?>

You may want to remove the list elements or style them as per your need.

*Edit - you to change this: post_not_in to this post__not_in in your query

1
  • It is showing only one post. Is it possible to show all posts of the same category on a category page?
    – Rahul
    Commented Aug 29, 2019 at 6:08
1

you can use this code to get related posts from the same category

$args = array(
                'category__in' => wp_get_post_categories( get_queried_object_id() ),
                'posts_per_page' => 5,
                'orderby'       => 'rand',
                'post__not_in' => array( get_queried_object_id() )
                );
    $the_query = new WP_Query( $args );

    if ( $the_query->have_posts() ) : ?>

        <ul class="">
        <!-- the loop -->
        <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>

            <li>
                <h6>
                    <a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>">
                    <?php the_title(); ?>
                    </a>
                </h6>
            </li>

        <?php endwhile; ?>
        <!-- end of the loop -->
        </ul>

        <?php wp_reset_postdata(); ?>

     <?php endif; ?>

and use this code to get related posts from the same tags

$tags = wp_get_post_terms( get_queried_object_id(), 'post_tag', ['fields' => 'ids'] );
    $args = [
        'post__not_in'        => array( get_queried_object_id() ),
        'posts_per_page'      => 5,
        'orderby'             => 'rand',
        'tax_query' => [
            [
                'taxonomy' => 'post_tag',
                'terms'    => $tags
            ]
        ]
    ];
    $the_query = new WP_Query( $args );
    if ( $the_query->have_posts() ) : ?>

        <ul class="">
        <!-- the loop -->
        <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>

            <li>
                <h6>
                    <a href="<?php the_permalink(); ?>" title="<?php the_title_attribute(); ?>">
                    <?php the_title(); ?>
                    </a>
                </h6>
            </li>

        <?php endwhile; ?>
        <!-- end of the loop -->
        </ul>

        <?php wp_reset_postdata(); ?>

    <?php endif; ?>
0

This answer makes sure that related posts are ordered by how many tags match.

For example if an article has 3 tags and there is another article that has the exact same 3 tags it should appear at the top of the list. Secondary sorting should be by post date so newer content is favored.

/**
 * Select content with common tags.
 * Sort so content with multiple matching tags are at the top.
 * Secondary sort on most recent content first.
 *
 * @param $post_id
 * @param int $limit
 * @return array
 */
function related_posts($post_id, $limit = 5) {

    global $wpdb;

    $query  = "SELECT TOP %d x.object_id as ID
FROM (
SELECT TOP 10 tr1.object_id, COUNT(tr1.term_taxonomy_id) AS common_tag_count
FROM {$wpdb->term_relationships} AS tr1
INNER JOIN {$wpdb->term_relationships} AS tr2 ON tr1.term_taxonomy_id = tr2.term_taxonomy_id
WHERE tr2.object_id = %d
GROUP BY tr1.object_id
HAVING tr1.object_id != %d
ORDER BY COUNT(tr1.term_taxonomy_id) DESC
) x
INNER JOIN {$wpdb->posts} p ON p.ID = x.object_id
ORDER BY common_tag_count DESC, p.post_date DESC;";

    $query = $wpdb->prepare($query, $limit, $post_id, $post_id);
    $ids = $wpdb->get_col($query);
    $posts = [];
    foreach($ids as $id)  {
        $posts[] = get_post($id);
    }

    return $posts;

}

The inner query here is to select the content with the most matching tags, and then the outer query is just used to to apply secondary sorting by post date.

Note this query is written for SQL Server so some syntax may need updating (e.g. TOP vs LIMIT).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.