3

I am trying to build a villa rentals website. To achieve that, I have created two custom post types. The one is named "villa" and is used to show the villas for rent, and the other one is named "destinations" and is used to show the places where this villas are.

I used the CPT-onomies plugin to make the "destinations" custom post type, a custom taxonomy for my custom post type "villa". There are two custom post types and the one is used as a taxonomy for the other one, to show where is that villa. On each single-villa.php page I want to show one related villa in the end of the post (after the loop), I want that related villa to be related only by destination (which is a custom taxonomy).

I found this article, I followed the instructions and it somehow did the trick for me, unfortunately I cannot figure out two things: 1. First I dont know how to make it show only one related villa (post) instead of all the related posts. 2. I dont know how to to exclude the current post from related posts (there are instructions on how to achieve that on the article I have linked to above but it doen't work for me.)

I added this code to functions.php

// Create a query for the custom taxonomy
    function related_posts_by_taxonomy( $post_id, $taxonomy, $args=array() ) {
        $query = new WP_Query();
        $terms = wp_get_object_terms( $post_id, $taxonomy );

        // Make sure we have terms from the current post
        if ( count( $terms ) ) {
            $post_ids = get_objects_in_term( $terms[0]->term_id, $taxonomy );
            $post = get_post( $post_id );
            $post_type = get_post_type( $post );

            // Only search for the custom taxonomy on whichever post_type
            // we AREN'T currently on
            // This refers to the custom post_types you created so
            // make sure they are spelled/capitalized correctly
            if ( strcasecmp($post_type, 'sites') == 0 ) {
                $type = 'sites';
            } else {
                $type = 'sites';
            }

            $args = wp_parse_args( $args, array(
                    'post_type' => $type,
                    'post__in' => $post_ids,
                    'taxonomy' => $taxonomy,
                    'term' => $terms[0]->slug,
                ) );
            $query = new WP_Query( $args );
        }

        // Return our results in query form
        return $query;
    }

And this code to single-villa.php

<?php $related =  related_posts_by_taxonomy( $post->ID, 'destinations' );
        while ( $related->have_posts() ): $related->the_post(); ?>

            <div class="inner">
                <a href="<?php get_permalink(); ?>"><?php the_post_thumbnail(); ?>  </a>
                <?php echo get_the_title(); ?>      

        </div>

        <?php endwhile; ?>

I dont want to use any plugin, instead I want to add some code to my theme to do that.

Thank you all in advance for your time and answers! I would like to apologise but English is not my native language! Thank you for your understanding.

3
  • make the "destinations" custom post type a custom taxonomy for my "villa" post type ...say what? :)
    – Rarst
    May 15, 2015 at 17:30
  • Please correct your post, your terminology is extremely confusing as @Rarst pointed out. Also, provide your code so that we can have a look at it. May 16, 2015 at 3:32
  • I have edited the post. I apologise but English is not my native language. I hope my edits will make my post easier to understand. Thank you!
    – BigDropGR
    May 16, 2015 at 20:12

2 Answers 2

6

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();
    }
}
13
  • Thank you very much for your answer. I'll give it a try and let you know!
    – BigDropGR
    May 17, 2015 at 9:36
  • Hi, this might sound a stupid question but do I have to edit anything in the code above (the one that needs to be add to the functions.php) to make it work? I copy pasted as it is without and now I only get white page if I reload my site. It doent show anything just white page. Thank you.
    – BigDropGR
    May 18, 2015 at 8:07
  • There is a syntax error somewhere then most probably. Let me recheck everything, as I said, it is untested. May 18, 2015 at 8:13
  • Hi Pieter, my php version is 5.5.9
    – BigDropGR
    May 18, 2015 at 9:51
  • Ok, then I must have a syntax error somewhere. What error do you get May 18, 2015 at 9:52
0

So to achieve your 2 issues all we have to do is modify the $args array that is being used for the Query. Because this is a Query we can use any of the parameters that Wordpress has kindly provided for us here: WP_Query.

Like this one: post__not_in (array) - use post ids. Specify post NOT to retrieve.

And this one: posts_per_page (int) - number of post to show per page

Now lets add these 2 parameters to the $args array.

$args = wp_parse_args( $args, array(
    'post_type' => $type,
    'post__in' => $post_ids,
    'taxonomy' => $taxonomy,
    'term' => $terms[0]->slug,
    'post__not_in' => $post_id, // $post_id has already been provided for us.
    'posts_per_page' => 1,
) );

And that should be it. This will also probably show the most recent post of the taxonomy, so you will probably have the same 'villa' showing up alot. But you can set the orderby parameter to fix that.

1
  • if you want ALL the related posts use 'nopaging' => true which will override posts_per_page or numberposts -- warning: it will return as many posts as it matches based on the arguments.
    – aequalsb
    May 17, 2015 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.