Custom taxonomy template for multiple custom post types

I’ve registered a custom taxonomy for 3 custom post types to share, this all works fine.

My issue at the moment is defining a conditional statement within the custom taxonomy file.

Read More

I want this taxonomy-{taxonomy}.php file to output different html depending on which custom post type the user is viewing. However, the taxonomy-{taxonomy}.php file seems to just grab all the posts which share that taxonomy.

For example:

register_taxonomy( 'City', array('Hotel', 'Gym', 'Supermarket'), array(
     'labels' => array(
        'name' => __( 'Cities' ),
        'singular_name' => __( 'City'  )), 
'hierarchical' => true ) );

These 3 custom post types (‘Hotel’, ‘Gym’, ‘Supermarket’) will share the City taxonomy.

Is it possible to display only Hotel posts and the Hotel sidebar when a user clicks the city taxonomy link from within a hotel post.

At the moment it seems like having 1 taxonomy shared between various posts means that it’s unable to detect which post type it should be displaying, and instead just outputs any post type which has the matching city taxonomy.

Any help would be much appreciated!

Related posts

2 comments

  1. When you land in a taxonomy archive page, e.g. the one with url http://yoursite.com/city/london/ WordPress can’t know if you want to see London’s Hotels, Gym of Supermarkets: you have to inform it.

    How inform WordPress? By url. So, if your url appear like http://yoursite.com/city/london/show/gym now WordPress understand you want to see only gyms…

    How to get that results? What you need is an endpoint:

    function my_city_endpoint() {
        add_rewrite_endpoint( 'show', EP_ALL );
    }
    add_action( 'init', 'my_city_endpoint' );
    

    In this way when you visit an url like the one posted above, WordPress set a query var 'show' that contain your post type, and you can make use of it hooking pre_get_posts action hook.

    function my_city_mod_query( $query ) {
        if ( is_main_query() && ! is_admin() && is_tax('city') ) {
            $post_type = $query->get('show');
            if ($post_type) $query->set('post_type', $post_type );
        }
    }
    add_action( 'pre_get_posts', 'my_city_mod_query' );
    

    Now your the taxonomy archive will show only the post type you set in the url via the endpoint.

    If you want to change also the html displayed, you have to change also the template file.

    Regarding sidebars you can register 4 sidebar called ‘sidebar-city’ used when no type is passed via url, and three other sidebars called 'sidebar-city-gym', 'sidebar-city-hotel' and 'sidebar-city-supermarket'.

    Then in template you can use a condition to show the right sidebar:

    $type = get_query_var('show');
    $type = ( empty($type) ) ? '' : '-' . $type;
    dynamic_sidebar( 'sidebar-city' . $type );
    

    If you even want to customize the way the single item in the loop is displayed (the htnl markup) you can use get_template_part and create 3 different template files for the single post type, so in your taxonomy-city.php you’ll have something like

    $type = get_query_var('show');
    
    while( have_posts() ) : the_post();
    
     if ( empty( $type ) ) { // no type is passed in the url, use standard markup
        ?>
        <h3><?php the_title(); ?></h3>
        <p><?php the_content('continue reading...'); ?></p>
        <?php
     } else {
        get_template_part('city-item', $type);
     }
    
    endwhile;
    

    Using this code, if no type is passed in the url, the template use stardard markup, on the countrary if a type is passed in the url, the template file require one of these files: 'city-item-gym.php', 'city-item-hotel.php' and 'city-item-supermarket.php' depending on type required, so you can full customize the markup.

    Of course, if the markup change from type to type is little, you can use some if or switch statements to change what is outputted.

    Now you need only last thing: generate the right url in the right page.

    You need a filter that should be fired only in post single view for the cpt involved.

    add_action('template_redirect', 'maybe_change_term_url');
    
    function maybe_change_term_url() {
       if ( is_single() ) {
         $cpts = array('gym','hotel','supermarket');
         if ( in_array(get_post_type(), $cpts) ) {
           add_filter('term_link', 'change_term_link', 10, 3);
         }
       }
    }
    
    function change_term_link( $link, $term, $taxonomy ) {
        $cpts = array('gym','hotel','supermarket');
        $type = get_post_type();
        if ( in_array($type, $cpts) && $taxonomy == 'city' ) {
           return trailingslashit($link) . 'show/' .  $type;
        }
        return $link;
    }
    

    As you can see I’ve used template_redirect hook to run a function that check the current page, and if in single post for one of the involved CPTs set the filter using 'term_link' filter, in this way the part '/show/gym', '/show/hotel' or ‘/show/supermarket' is automatically added to links and everything go in right place.

    Please note that all the code here is completely untested and wrote here without any syntax highlight so probably you’ll found some errors or typo…

  2. I believe what you are thinking/wanting that the taxonomy-{taxonomy}.php template will filter by CPT. By default the listing of all items within a taxonomy is core functionality while additional filters can be accomplished with the pre_get_posts hook or rewriting the main query in the template.

    By using a combination of query vars, pre_get_posts and term_link hooks you can filter the results on the template. First, using the filter term_link for the function of get_term_link you can filter the link that is being generated to create the appropriate filter for CPT you wish. Or you can hardcode the links as you need to build the proper filtering. Then setup a query var you can provide an additional filter to the taxonomy. Once set filter the tax queries with the pre_get_posts hook by the specified post type (as the value of the query value).

Comments are closed.