Scenario
I am using a complex, awesomely crafted tax query to get up to 4 related posts, first going by tag, then by category if there are not enough posts in tag-related query. This was finely addressed here.
I am now trying to do another extension of this function to do a 3rd query when necessary that gets all "post-type related posts."
What I Have So Far
I need to do this because we use many custom post types which have no tags or category associations at all -- I did not realize this when looking for original solution and its needed because not only does this function currently break the bottom of the page on custom post_types which are not associated with a tag or a category, due to
Fatal error: Call to a member function have_posts() on a non-object in...
but of course also because without including the custom post_type
s, the Related Posts come out empty.
I have this so far but running into a snag and I can't tell why:
function get_max_related_posts( $recent_posts = '', $taxonomy_1 = 'post_tag', $taxonomy_2 = 'category', $total_posts = 4 ) {
// First, make sure we are on a single page, if not, bail
if ( !is_single() )
return false;
// Sanitize and vaidate our incoming data
if ( 'post_tag' !== $taxonomy_1 ) {
$taxonomy_1 = filter_var( $taxonomy_1, FILTER_SANITIZE_STRING );
if ( !taxonomy_exists( $taxonomy_1 ) )
return false;
}
if ( 'category' !== $taxonomy_2 ) {
$taxonomy_2 = filter_var( $taxonomy_2, FILTER_SANITIZE_STRING );
if ( !taxonomy_exists( $taxonomy_2 ) )
return false;
}
if ( 4 !== $total_posts ) {
$total_posts = filter_var( $total_posts, FILTER_VALIDATE_INT );
if ( !$total_posts )
return false;
}
// Everything checks out and is sanitized, lets get the current post
$current_post = sanitize_post( $GLOBALS['wp_the_query']->get_queried_object() );
// Lets get the first taxonomy's terms belonging to the post
$terms_1 = get_the_terms( $current_post, $taxonomy_1 );
// Set a varaible to hold the post count from first query
$count = 0;
// Set a variable to hold the results from query 1
$q_1 = [];
// Make sure we have terms
if ( $terms_1 ) {
// Lets get the term ID's
$term_1_ids = wp_list_pluck( $terms_1, 'term_id' );
$exclude = array_merge( [$current_post->ID], $recent_posts );
// Lets build the query to get related posts
$args_1 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $total_posts,
'fields' => 'ids',
'tax_query' => [
[
'taxonomy' => $taxonomy_1,
'terms' => $term_1_ids,
'include_children' => false
]
],
];
$q_1 = get_posts( $args_1 );
// Count the total amount of posts
$q_1_count = count( $q_1 );
// Update our counter
$count = $q_1_count;
}
// We will now run the second query if $count is less than $total_posts
if ( $count < $total_posts ) {
$terms_2 = get_the_terms( $current_post, $taxonomy_2 );
// Make sure we have terms
if ( $terms_2 ) {
// Lets get the term ID's
$term_2_ids = wp_list_pluck( $terms_2, 'term_id' );
// Calculate the amount of post to get
$diff = $total_posts - $count;
// Create an array of post ID's to exclude
if ( $q_1 ) {
$exclude = array_merge( $exclude, $q_1 );
}
$args_2 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $diff,
'fields' => 'ids',
'tax_query' => [
[
'taxonomy' => $taxonomy_2,
'terms' => $term_2_ids,
'include_children' => false
]
],
];
$q_2 = get_posts( $args_2 );
if ( $q_2 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_2 );
}
if ( $count < $total_posts ) {
// Calculate the amount of post to get
$diff = $total_posts - $count;
// Create an array of post ID's to exclude
if ( $q_2 ) {
$exclude = array_merge( $exclude, $q_2 );
}
$args_3 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $diff,
'fields' => 'ids'
];
$q_3 = get_posts( $args_3 );
if ( $q_3 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_3 );
}
}
}
}
// Make sure we have an array of ID's
if ( !$q_1 )
return false;
// Run our last query, and output the results
$final_args = [
'ignore_sticky_posts' => 1,
'post_type' => $current_post->post_type,
'posts_per_page' => count( $q_1 ),
'post__in' => $q_1,
'order' => 'ASC',
'orderby' => 'post__in',
'suppress_filters' => true,
'no_found_rows' => true
];
$final_query = new WP_Query( $final_args );
return $final_query;
}
The Problem
I honestly can't tell what the problem is, as when I try
$args_3 = [
'post_type' => 'gear_post',
'posts_per_page' => 5,
'fields' => 'ids'
];
$q_3 = get_posts( $args_3 );
On the live page, it out puts the proper post IDs which I need to feed into the final WP_Query.
In the above, long and sweet function, I think I am feeding the IDs properly:
if ( $q_2 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_2 );
}
if ( $count < $total_posts ) {
// Calculate the amount of post to get
$diff = $total_posts - $count;
// Create an array of post ID's to exclude
if ( $q_2 ) {
$exclude = array_merge( $exclude, $q_2 );
}
// THIS IS THE 3rd QUERY that's I'm not getting right
// THIS IS THE 3rd QUERY that's I'm not getting right
// THIS IS THE 3rd QUERY that's I'm not getting right
$args_3 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $diff,
'fields' => 'ids'
];
$q_3 = get_posts( $args_3 );
if ( $q_3 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_3 );
// THIS IS THE 3rd QUERY that's I'm not getting right
// THIS IS THE 3rd QUERY that's I'm not getting right
// THIS IS THE 3rd QUERY that's I'm not getting right
}
}
}
}
// Make sure we have an array of ID's
if ( !$q_1 )
Yet when I try to run the function on that page, I get this error:
Fatal error: Call to a member function have_posts() on a non-object in...
Any idea what I am doing wrong?
Solution - Final Version
Using Peter's sweet answer, I found a couple minor bugs for my use case and also extended the function a bit more, to include any random "related" posts if tags, categories, and post type all come up empty.
My final version is like so:
function get_max_related_posts( $recent_posts = array(), $taxonomy_1 = 'post_tag', $taxonomy_2 = 'category', $total_posts = 4 ) {
// First, make sure we are on a single page, if not, bail
if ( !is_single() )
return false;
// Sanitize and vaidate our incoming data
if ( 'post_tag' !== $taxonomy_1 ) {
$taxonomy_1 = filter_var( $taxonomy_1, FILTER_SANITIZE_STRING );
if ( !taxonomy_exists( $taxonomy_1 ) )
return false;
}
if ( 'category' !== $taxonomy_2 ) {
$taxonomy_2 = filter_var( $taxonomy_2, FILTER_SANITIZE_STRING );
if ( !taxonomy_exists( $taxonomy_2 ) )
return false;
}
if ( 4 !== $total_posts ) {
$total_posts = filter_var( $total_posts, FILTER_VALIDATE_INT );
if ( !$total_posts )
return false;
}
// Everything checks out and is sanitized, lets get the current post
$current_post = sanitize_post( $GLOBALS['wp_the_query']->get_queried_object() );
// Lets get the first taxonomy's terms belonging to the post
$terms_1 = get_the_terms( $current_post, $taxonomy_1 );
// Set a varaible to hold the post count from first query
$count = 0;
// Set a variable to hold the results from query 1
$q_1 = [];
// Set a variable to hold the exclusions
$sticky = get_option( 'sticky_posts' );
$exclude = array_merge( [$current_post->ID], $sticky );
$exclude = array_merge( $exclude, $recent_posts );
// Make sure we have terms
if ( $terms_1 ) {
// Lets get the term ID's
$term_1_ids = wp_list_pluck( $terms_1, 'term_id' );
// Lets build the query to get related posts
$args_1 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $total_posts,
'fields' => 'ids',
'tax_query' => [
[
'taxonomy' => $taxonomy_1,
'terms' => $term_1_ids,
'include_children' => false
]
],
];
$q_1 = get_posts( $args_1 );
// Update our counter
$count = count( $q_1 );
// Update our counter
$exclude = array_merge( $exclude, $q_1 );
}
// We will now run the second query if $count is less than $total_posts
if ( $count < $total_posts ) {
$terms_2 = get_the_terms( $current_post, $taxonomy_2 );
// Make sure we have terms
if ( $terms_2 ) {
// Lets get the term ID's
$term_2_ids = wp_list_pluck( $terms_2, 'term_id' );
// Calculate the amount of post to get
$diff = $total_posts - $count;
$args_2 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $diff,
'fields' => 'ids',
'tax_query' => [
[
'taxonomy' => $taxonomy_2,
'terms' => $term_2_ids,
'include_children' => false
]
],
];
$q_2 = get_posts( $args_2 );
if ( $q_2 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_2 );
// Update our post counter
$count = count( $q_1 );
// Update our counter
$exclude = array_merge( $exclude, $q_2 );
}
}
}
// We will now run the third query if $count is less than $total_posts
if ( $count < $total_posts ) {
// Calculate the amount of post to get
$diff = $total_posts - $count;
$args_3 = [
'post_type' => $current_post->post_type,
'post__not_in' => $exclude,
'posts_per_page' => $diff,
'fields' => 'ids',
];
$q_3 = get_posts( $args_3 );
if ( $q_3 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_3 );
} else {
$args_4 = [
'post_type' => 'any',
'post__not_in' => $exclude,
'posts_per_page' => $diff,
'fields' => 'ids',
];
$q_4 = get_posts( $args_4 );
if ( $q_4 ) {
// Merge the two results into one array of ID's
$q_1 = array_merge( $q_1, $q_4 );
}
}
}
// Make sure we have an array of ID's
if ( !$q_1 )
return false;
// Run our last query, and output the results
$final_args = [
'ignore_sticky_posts' => 1,
'post_type' => 'any',
'posts_per_page' => count( $q_1 ),
'post__in' => $q_1,
'order' => 'ASC',
'orderby' => 'post__in',
'suppress_filters' => true,
'no_found_rows' => true
];
$final_query = new WP_Query( $final_args );
return $final_query;
}
Mainly, I fixed the exclusions mechanism, extended in one more time to any posts, and also changed the "post_type" in the final query arguments to 'any' so that the final query is not filtered by the custom post type. Again, thank you Peter!