|
<?php |
|
/** |
|
* A WordPress helper function that takes the ID of the current post |
|
* and returns the top three related posts ranked by highest number of taxonomy |
|
* terms in common with the current post. Alternately, you can modify lines 26-33 |
|
* to exclude certain taxonomies so that we only check for terms in specific taxonomies |
|
* to determine the related posts. To include up to the top X related posts instead of |
|
* up to three, you can modify lines 149-150. |
|
* |
|
* In your template to make use of this function you would do something like... |
|
* |
|
* $current_post_id = get_the_id(); |
|
* $related_post_ids = get_related_posts($current_post_id); |
|
* |
|
*/ |
|
|
|
function get_related_posts($current_post_id) |
|
{ |
|
// Get the post type we're dealing with based on the current post ID. |
|
$post_type = get_post_type($current_post_id); |
|
|
|
// Get all taxonomies of the specified post type of the current post. |
|
$taxonomies = []; |
|
$taxonomy_objects = get_object_taxonomies( $post_type, 'objects' ); |
|
foreach($taxonomy_objects as $taxonomy) { |
|
// If you want to only check against certain taxonomies, modify this section as needed |
|
// to set conditions for which taxonomies should be excluded or included. Below is just an example. |
|
// if ($taxonomy->name !== 'post_format' && $taxonomy->name !== 'post_tag') { |
|
// array_push($taxonomies, $taxonomy); |
|
// } |
|
|
|
// By default, we will check against all taxonomies. |
|
array_push($taxonomies, $taxonomy); |
|
} |
|
|
|
// Get all the posts of the specified post type, |
|
// excluding the current post, so that we can compare these |
|
// against the current post. |
|
$other_posts_args = array( |
|
'posts_per_page' => -1, |
|
'post_type' => $post_type, |
|
'post__not_in' => array($current_post_id), |
|
); |
|
$other_posts = new WP_Query( $other_posts_args ); |
|
|
|
wp_reset_postdata(); |
|
|
|
// We will create an object for each matching post that will include |
|
// the ID and count of the number of times it matches any taxonomy term with the current post. |
|
// Later, when we create those, they will get pushed to this $matching_posts array. |
|
$matching_posts = array(); |
|
|
|
// If we have other posts, loop through them and |
|
// count matches for any taxonomy terms in common. |
|
if($other_posts->have_posts()) { |
|
|
|
foreach($taxonomies as $taxonomy) { |
|
|
|
// Get the term IDs of terms for the current post |
|
// (the post presumably displaying as a single post |
|
// back in our template, for which were finding related posts). |
|
$current_post_terms = get_the_terms($current_post_id, $taxonomy->name); |
|
|
|
|
|
// Only continue if the current post actually has some terms for this taxonomy. |
|
if($current_post_terms !== false) { |
|
|
|
foreach($other_posts->posts as $post) { |
|
|
|
// Get the term IDs of terms for this taxonomy |
|
// for the other post we are currently looping over. |
|
$other_post_terms = get_the_terms($post->ID, $taxonomy->name); |
|
|
|
// Check that other post has terms and only continue if there |
|
// are terms to compare. |
|
if($other_post_terms !== false) { |
|
|
|
$other_post_term_IDs = array(); |
|
$current_post_term_IDs = array(); |
|
|
|
// Get term IDs from each term in the current post. |
|
foreach($current_post_terms as $term) { |
|
array_push($current_post_term_IDs, $term->term_id); |
|
} |
|
|
|
// Get term IDs from each term in the other post. |
|
foreach($other_post_terms as $term) { |
|
array_push($other_post_term_IDs, $term->term_id); |
|
} |
|
|
|
if( !empty($other_post_term_IDs) && !empty($current_post_term_IDs) ) { |
|
|
|
// Collect the matching term IDs for the terms the posts have in common. |
|
$match_count = sizeof(array_intersect($other_post_term_IDs, $current_post_term_IDs)); |
|
|
|
// Get the ID of the other post to use to identify and store this post |
|
// in our results. |
|
$post_ID = $post->ID; |
|
|
|
if ($match_count > 0) { |
|
|
|
// Assume post not added previously. |
|
$post_already_added = false; |
|
|
|
// If posts have already been added to our matches |
|
// then check to see if we already added this post. |
|
if(!empty($matching_posts)) { |
|
|
|
foreach($matching_posts as $post) { |
|
// If this post was added previously then let's increment the count |
|
// for our new matching terms. |
|
if (isset($post->ID) && $post->ID == $post_ID) { |
|
$post->count += $match_count; |
|
// Switch this to true for the check we perform below. |
|
$post_already_added = true; |
|
} |
|
} |
|
|
|
// If never found a post with same ID in our $matching_posts |
|
// list then create a new entry associated with this post and add it. |
|
if ($post_already_added === false) { |
|
$new_matching_post = new stdClass(); |
|
$new_matching_post->ID = $post_ID; |
|
$new_matching_post->count = $match_count; |
|
array_push($matching_posts, $new_matching_post); |
|
} |
|
} else { |
|
// If no posts have been added yet to $matching_posts then this will be the first. |
|
$new_matching_post = new stdClass(); |
|
$new_matching_post->ID = $post_ID; |
|
$new_matching_post->count = $match_count; |
|
array_push($matching_posts, $new_matching_post); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
if(!empty($matching_posts)) { |
|
// Sort the array in order of highest count for total terms in common |
|
// (most related to least). |
|
usort($matching_posts, function($a, $b) { |
|
return strcmp($b->count, $a->count); |
|
}); |
|
|
|
// Just take the top 3 most related |
|
$most_related = array_slice($matching_posts, 0, 3); |
|
|
|
// Get the IDs of most related posts. |
|
$matching_posts = array_map(function($obj) { |
|
return $obj->ID; |
|
}, $most_related); |
|
} |
|
|
|
} |
|
|
|
return $matching_posts; |
|
|
|
|
|
} |
So this whole thread is very helpful but there are two minor issues.
First the WP_Query on line 39 does not pass the post_per_page argument which by default is 9. This will restrict the total number of post you can compare against to only the first 9 returned by the query.
The fix for this is setting the post_per_page to -1 which in a WP query returns all the posts stored in your database. Add the following line to the arguments after line 39
'posts_per_page' => -1,
The second issue is more of a personal formatting thing.
Obviously you can output posts however you like and then style them with CSS but here's an example of just spitting out the posts as hyperlinks.
Long story short I had to help a friend with their site and while this thread got them 90% of the way there I just wanted to let people know about the potential remaining issues you might face if you just copy paste the above solution.
Here is the code in full: