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 1794.

No Hooks

This function does not have any filters or actions available. Request one?
PHP
        public function related_posts($post_id=null, $args=array()){
            $this->timer('Related Posts');

            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);

            //Potential candidate for new Nebula transient() function
            $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 ){
                $this->timer('Related Posts', 'end');
                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'])),
            );

            $this->timer('Related Posts', 'end');
            return new WP_Query($query_options);
        }

Override

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