Skip to Content

related_posts()

Retrieve related posts based on term frequency.

PHP March 5, 2019

Usage

PHP
related_posts($post_id, $args)

Parameters

$post_id
(Required) (int) The post ID to find related posts of
Default: None

$args
(Optional) (array) An array of additional options
Default: None

Parameter Notes

Options for the $args array:

taxonomy
Default: post_tag

post_type
Default: array('post')

max
Default: 5

Request or provide clarification »

Examples

PHP
<h4>Related Posts</h4>
<?php $related_posts = nebula()->related_posts(get_the_id()); ?>
<?php if ( $related_posts->have_posts() ): ?>
        <ul>
            <?php while ( $related_posts->have_posts() ): ?>
                <?php $related_posts->the_post(); ?>
                <li><a href="<?php the_permalink();?>"><h2><?php the_title();?></h2></a></li>
            <?php endwhile; ?>
            <?php wp_reset_postdata(); ?>
        </ul>
<?php endif; ?>

Additional Notes

Results are stored in a transient for 24 hours for optimization.

Source File

Located in /libs/Functions.php on line 1706.

No Hooks

This function does not have any filters or actions available. Request one?
PHP
    public function related_posts($post_id=null, $args=array()){
        global $post, $wpdb;

        $post_id = intval($post_id);
        if ( !$post_id && $post->ID ){
            $post_id = $post->ID;
        }

        if ( !$post_id ){
            return false; //Post ID is required for this function
        }

        $defaults = array(
            'taxonomy' => 'post_tag',
            'post_type' => array('post'),
            'max' => 5
        );
        $options = wp_parse_args($args, $defaults);

        $related_post_ids = get_transient('nebula-related-' . $options['taxonomy'] . '-' . $post_id);
        if ( empty($related_post_ids) || $this->is_debug() ){
            $term_args = array(
                'fields' => 'ids',
                'orderby' => 'count', //Sort by frequency
                'order' => 'ASC' //Least popular to most popular
            );

            $orig_terms_set = wp_get_object_terms($post_id, $options['taxonomy'], $term_args);
            $orig_terms_set = array_map('intval', $orig_terms_set); //Make sure each returned term id to be an integer.
            $terms_to_iterate = $orig_terms_set; //Store a copy that we'll be reducing by one item for each iteration.

            $post_args = array(
                'fields' => 'ids',
                'post_type' => $options['post_type'],
                'post__not_in' => array($post_id),
                'posts_per_page' => 50 //Start with more than enough posts
            );

            $related_post_ids = array();

            //Loop through the terms to find posts that contain multiple terms (term1 AND term2 AND term3)
            while ( count($terms_to_iterate) > 1 ){
                $post_args['tax_query'] = array(
                    array(
                        'taxonomy' => $options['taxonomy'],
                        'field' => 'id',
                        'terms' => $terms_to_iterate,
                        'operator' => 'AND'
                    )
                );

                $posts = get_posts($post_args);
                foreach( $posts as $id ){
                    $id = intval($id);
                    if ( !in_array($id, $related_post_ids) ){
                        $related_post_ids[] = $id;
                    }
                }

                array_pop($terms_to_iterate); //Remove the least related post ID
            }

            $post_args['posts_per_page'] = $options['max']; //Reduce the number to our desired max
            $post_args['tax_query'] = array(
                array(
                    'taxonomy' => $options['taxonomy'],
                    'field' => 'id',
                    'terms' => $orig_terms_set
                )
            );

            //Check for posts that contain any of the terms (to fill out the desired max)
            $posts = get_posts($post_args);
            foreach ( $posts as $count => $id ){
                $id = intval($id);
                if ( !in_array($id, $related_post_ids) ){
                    $related_post_ids[] = $id;
                }

                if ( count($related_post_ids) > $options['max'] ){
                    break; //We have enough related post IDs now, stop the loop.
                }
            }

            set_transient('nebula-related-' . $options['taxonomy'] . '-' . $post_id, $related_post_ids, DAY_IN_SECONDS);
        }

        if ( !$related_post_ids ){
            return false;
        }

        //Query for the related post IDs
        $query_options = array(
            'post__in' => $related_post_ids,
            'orderby' => 'post__in',
            'post_type' => $options['post_type'],
            'posts_per_page' => min(array(count($related_post_ids), $options['max'])),
        );

        return new WP_Query($query_options);
    }

Override

This function can not be short-circuited with an override filter. Request one?