Show related posts on single page by custom taxonomy on custom post

I would just just scrap the function above as there are several bugs in the code and is also not quite effecient. I am actually surpriced that it really works for you.

Your best solution here would be to write a complete new function. Here is want we want to do and how we are going to accomplish this

  • Get the current post object on the single post page. $post is unreliable, so we are going to use the main query object here which we will return with get_queried_object(). From here we can use the post ID and post type to get other related info

  • Get the terms of the current post being viewed with wp_get_post_term(). We will set the fields paremeter to only get the term ids. This returnes array of term ids can then be used in our tax_query

  • Add in some validation to validate the user input and also sanitize the input

Lets put that all in code (CAVEAT: This is all untested and requires at least PHP 5.4+)

function get_related_posts( $taxonomy = '', $args = [] )
{
    /*
     * Before we do anything and waste unnecessary time and resources, first check if we are on a single post page
     * If not, bail early and return false
     */
    if ( !is_single() )
        return false;

    /*
     * Check if we have a valid taxonomy and also if the taxonomy exists to avoid bugs further down.
     * Return false if taxonomy is invalid or does not exist
     */
    if ( !$taxonomy ) 
        return false;

    $taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
    if ( !taxonomy_exists( $taxonomy ) )
        return false;

    /*
     * We have made it to here, so we should start getting our stuff togther. 
     * Get the current post object to start of
     */
    $current_post = get_queried_object();

    /*
     * Get the post terms, just the ids
     */
    $terms = wp_get_post_terms( $current_post->ID, $taxonomy, ['fields' => 'ids'] );

    /*
     * Lets only continue if we actually have post terms and if we don't have an WP_Error object. If not, return false
     */
    if ( !$terms || is_wp_error( $terms ) )
        return false;

    /*
     * Set the default query arguments
     */
    $defaults = [
        'post_type' => $current_post->post_type,
        'post__not_in' => [$current_post->ID],
        'tax_query' => [
            [
                'taxonomy' => $taxonomy,
                'terms' => $terms,
                'include_children' => false
            ],
        ],
    ];

    /*
     * Validate and merge the defaults with the user passed arguments
     */
    if ( is_array( $args ) ) {
        $args = wp_parse_args( $args, $defaults );
    } else {
        $args = $defaults;
    }

    /*
     * Now we can query our related posts and return them
     */
    $q = get_posts( $args );

    return $q;
}

Now that we have a better function in place, we can use it in our single post page or content template parts depending on your exact use case. As you might have noticed, our new function get_related_posts() has two parameters, the first one which accepts a single taxonomy value, and the second an array of arguments. This arguments will the arguments passed to our query, so you can pass any valid array of arguments that is acceptible to WP_Query and get_posts here.

Example:

You need one post to be returned, so you can try the following: (Please note, do not use the post type parameter or any of the taxonomy type parameters here, you might get unexpected output)

if ( function_exists( 'get_related_posts' ) ) {
    $related_posts = get_related_posts( 'my_taxonomy_name', ['posts_per_page' => 1] );
    if ( $related_posts ) {
        foreach ( $related_posts as $post ) {
            setup_postdata( $post ); 
            // Use your template tags and html mark up as normal like
            the_title();
            the_content();
            // etc etc
        }
        wp_reset_postdata();
    }
}

EDIT

From comments it seems that your PHP version is older than 5.4 which does not support the new short array syntax ([]), and therefor you get the dreaded WSOD. For this to work, you need change the new array syntax to the old syntax (array()).

You can try the following:

function get_related_posts( $taxonomy = '', $args = array() )
{
    /*
     * Before we do anything and waste unnecessary time and resources, first check if we are on a single post page
     * If not, bail early and return false
     */
    if ( !is_single() )
        return false;

    /*
     * Check if we have a valid taxonomy and also if the taxonomy exists to avoid bugs further down.
     * Return false if taxonomy is invalid or does not exist
     */
    if ( !$taxonomy ) 
        return false;

    $taxonomy = filter_var( $taxonomy, FILTER_SANITIZE_STRING );
    if ( !taxonomy_exists( $taxonomy ) )
        return false;

    /*
     * We have made it to here, so we should start getting our stuff togther. 
     * Get the current post object to start of
     */
    $current_post = get_queried_object();

    /*
     * Get the post terms, just the ids
     */
    $terms = wp_get_post_terms( $current_post->ID, $taxonomy, array( 'fields' => 'ids') );

    /*
     * Lets only continue if we actually have post terms and if we don't have an WP_Error object. If not, return false
     */
    if ( !$terms || is_wp_error( $terms ) )
        return false;

    /*
     * Set the default query arguments
     */
    $defaults = array(
        'post_type' => $current_post->post_type,
        'post__not_in' => array( $current_post->ID),
        'tax_query' => array(
            array(
                'taxonomy' => $taxonomy,
                'terms' => $terms,
                'include_children' => false
            ),
        ),
    );

    /*
     * Validate and merge the defaults with the user passed arguments
     */
    if ( is_array( $args ) ) {
        $args = wp_parse_args( $args, $defaults );
    } else {
        $args = $defaults;
    }

    /*
     * Now we can query our related posts and return them
     */
    $q = get_posts( $args );

    return $q;
}

And then to use the code in templates, change to

if ( function_exists( 'get_related_posts' ) ) {
    $related_posts = get_related_posts( 'my_taxonomy_name', array( 'posts_per_page' => 1) );
    if ( $related_posts ) {
        foreach ( $related_posts as $post ) {
            setup_postdata( $post ); 
            // Use your template tags and html mark up as normal like
            the_title();
            the_content();
            // etc etc
        }
        wp_reset_postdata();
    }
}

Leave a Comment