How to get term link that crosses different custom post types?

In my WordPress project, I have several different custom post types and they all share a common taxonomy. I am in the final stage of the project and now is time to link all custom post types using a sidebar widget. The problem I am having is that I don’t know the appropriate way to get the term link linked to the correct custom post type.

For example, I have custom post types: book, author, product; and a taxonomy: genre that includes a horror category.

I would like to be able to get link structure as such:

/book/genre/horror
/product/genre/horror
/author/genre/horror

When I do get_term_link('horror');, I get one of the term links, it seems that one of the custom post type has preference. How can I manage to get each term link corresponding to the correct custom post type?

Hard coding it is never a good idea, so I was wondering if there is a proper way to go about this. This is an old issue I am having, and have done a lot of searching for a solution but I didn’t find it. I come here as the last resort. Thanks.

Solutions Collecting From Web of "How to get term link that crosses different custom post types?"

This code display all posts of all categories of genre taxonomy for custom post type book. Now, For different custom post type (author, product ) you have to change Custom Post Type Name inside $arg of WP_Query(). You will get term link using get_term_link($catterm) function or you can use also get_the_term_list().

 $args = array(
            'number'     => $number,
            'hide_empty' => $hide_empty,
            'include'    => $ids
        );

        $custom_categories = get_terms( 'genre ', $args );

        foreach ( $custom_categories as $catterm){

            $arg = Array( 
            'post_type' => 'book',
            'posts_per_page' => '-1',
            'post_status' => 'publish',
            'tax_query' => Array( Array ( 
            'taxonomy' => 'genre ' ,
            'terms' => $catterm->term_id
            )) );

        $loop = new WP_Query( $arg ); 
        global $post;
        while ( $loop->have_posts() ) : $loop->the_post();
        ?>

        <div class="gallery-content">
        <div class="entry-content">

        <?php 
         echo '<li>' . get_the_title() . '</li>';
         echo '<li><a href="'.get_term_link($catterm).'">'.$catterm->name.'</a></li>';   
        ?>  

        </div>
        </div>

       <?php endwhile;
        }   
        ?>

Good news, I figured out a way to accomplish this. Here is my solution.

Taking my example above, I will walk you through what I did to get example.com/book/genre/horror to point to custom post type book, taxonomy genre and category horror.

Pretty permalinks should be enabled. Go to Settings > Permalinks to enable it. Otherwise there is no point of doing this. So here we go…

1) Create a page called book (and slug book), note the page ID that was generated for that page after you have published it, you will need it ( you can see this in your URL, should be a ?post= with the page ID in it.)

2) Now we need to create a rewrite so that we can point the link to the correct template page. So, in your functions.php file (or your plugin file, up to you) you need to add this code:

function my_rewrite_rules() {

    add_rewrite_rule(
        'book/genre/([[A-Za-z0-9\-]+)?/?$',
        'index.php?page_id=1234&genre=$matches[1]',
        'top'
    );

    flush_rewrite_rules();
}
add_action( 'init', 'my_rewrite_rules' );

Now, replace 1234 with the page ID you generated for the page book. And go to any front end page and reload it.

Important: you don’t want to run flush_rewrite_rules(); every time a page is loaded in your site. This is a heavy function and will slow things down for you. You only need to run it once on the front end. After you reloaded any page on the front end, the rewrite rules are flushed and you can remove this line.

What this function does is, if a url is has the structure of /book/genre/ANYTHING_HERE/, WordPress will redirect user to the page with ID of 1234 with the data genre=ANYTHING_HERE. Note that ANYTHING_HERE is filtered through the regular expression provided, and that will only accept characters (upper-case and lower-case) from a-z, numbers 0-9 and dash (-).

3) Go to your theme folder and create a file called page-1234.php (replacing 1234 with your page ID, of course.) Now in this file you can do all sorts of fun stuff. Here is how you could list all books by genre:

<?php
get_header(); 

global $wp_query;
$genre = $wp_query->query_vars['genre'];

$args = array(
    'post_type' => 'book',
    'tax_query' => array(
        array(
            'taxonomy' => 'genre',
            'field'    => 'slug',
            'terms'    => $genre,
        ), ),
);

$books = new WP_Query( $args );
?>

<h1>Books of <?php echo $genre;?></h1>
<ul>
<?php foreach ( $books as $book ) : ?>
    <li><?php echo $book->post_title; ?></li>
<?php endforeach; ?>
</ul>

<?php get_footer(); ?>

In addition, if you are not working with taxonomies, like genre in this example, you could use your custom tags. By the way, the term genre only works in this example because it was registered as a taxonomy. If this wasn’t the case, you could add a new tag to WordPress, whatever you would like, by adding this to your functions.php as such:

function custom_rewrite_tag() {
  add_rewrite_tag('%food%', '([^&]+)');
}
add_action('init', 'custom_rewrite_tag', 10, 0);

Now you replace genre in the step 2 with the tag food. In your page-1234.php you can get the query_vars['food'] and manipulate it any way you like and run requests like example.com/book/food/chocolate. Note that some query_vars are reserved. Check the resources below for them.

To finish off, to get the links to point to the correct custom post type and taxonomy, you need a custom function. This is the only way I could think of doing this and it is something like this:

function get_books_category_link( $category_slug ) {

    $page = get_permalink( 1234 );
    $link = '';

    if ( '/%postname%/' == get_option('permalink_structure') ) {
        $link = $page . 'genre/' . $category_slug;
    } else {
        $link = add_query_arg( array( 'genre' => $category_slug ) , $page );
    }

    return $link;
}

And that’s it! Hope it helps. Some great reads below, specially the article from Stephen Harris really made a difference on understanding the rewrite API. Thanks.

Tuts+ The Rewrite API: The Basics

Rewrite API/add rewrite rule

WordPress Query Vars