WordPress – Creating multiple versions of the same single-customtype.php depending on selected taxonomy categories

Okay, the following code should do the trick for you:

function get_clients_correct_template($single_template) {
    global $post;

    if ( 'clients' == $post->post_type ) {
        $_template = false;
        // Get all the terms for this post
        $categories = wp_get_post_terms( $post->ID, 'clients_categories' );
        if ( $categories && ! is_wp_error( $categories ) ) {
            global $wp;
            // I guessed that the client category that's in the URL will be in the query, but it's not, so we have to get it from $wp->matched_query
            if ( preg_match( '~clients_categories=[\w|\d|-]*&?~', $wp->matched_query ) ) {
                $slug = preg_replace( '~.*?clients_categories=([\w|\d|-]*)&?.*?$~', '\1', $wp->matched_query );
                // See if the slug we found matches a slug of one of the client's categories
                foreach ( $categories as $cat ) {
                    if ( $cat->slug == $slug ) {
                        $_template = locate_template( array( "single-clients-{$slug}.php" ), false );
                        break;
                    }
                }
            }
            // If we haven't found a template yet, just assign the first found template
            if ( ! $_template ) {
                $_template = locate_template( array( "single-clients-{$categories[0]->slug}.php" ), false );
            }
            $single_template = $_template ? $_template : $single_template;
        }
    }

    return $single_template;
}
// This is pre WP 3.4
add_filter( "single_template", "get_clients_correct_template", 1000 );
// This is post WP 3.4
add_filter( "clients_template", "get_clients_correct_template", 1000 );

// Fixes the permalinks for the "clients" post type
function clients_custom_permalink($permalink, $post_id, $leavename) {
    if ( strpos( $permalink, 'client/' ) === FALSE || ! ( $post = get_post( $post_id ) ) || get_post_type( $post_id ) != 'clients' ) {
        return $permalink;
    }

    // Get taxonomy terms
    $terms = wp_get_object_terms( $post->ID, 'clients_categories' );
    if ( ! is_wp_error( $terms ) && ! empty( $terms ) && is_object( $terms[0] ) ) {
        $taxonomy_slug = false;
        if ( is_tax( 'clients_categories' ) ) {
            $term = get_queried_object();
            $taxonomy_slug = $term->slug;
        } else {
            $taxonomy_slug = $terms[0]->slug;
        }
    } else {
        $taxonomy_slug = 'no-category';
    }
    $replace_count = 1;

    return preg_replace( '~client/~', $taxonomy_slug . "https://wordpress.stackexchange.com/", $permalink, $replace_count );
}
add_filter('post_link', 'clients_custom_permalink', 10, 3);
add_filter('post_type_link', 'clients_custom_permalink', 10, 3);

// Fixes the URL's for taxonomy archives
function clients_categories_custom_permalink($permalink, $term, $taxonomy) {
    if ( $taxonomy != 'clients_categories' ) {
        return $permalink;
    }

    return home_url( "/{$term->slug}/" );
}
add_filter( 'term_link', 'clients_categories_custom_permalink', 10, 3 );

// Generates custom rewrite rules for recognizing the custom Permalink structure for both "clients" posts and "clients_categories" terms
function add_client_rewrite_rules( $wp_rewrite ) {
    static $did_rules = false;
    if ( ! $did_rules ) {
        $additional_rules = array();
        $terms = get_terms( 'clients_categories', array( 'hide_empty' => false ) );
        if ( $terms ) {
            foreach ( $terms as $term ) {
                // Add a rule for the taxonomy archives of the type "client-category-slug[/]" - the "https://wordpress.stackexchange.com/" is optional
                $additional_rules[ "({$term->slug})/?$" ] = 'index.php?clients_categories=$matches[1]';
                // Add a rule of the sort "client-category-slug/client-slug"
                $additional_rules[ "({$term->slug})/([^/]*)" ] = 'index.php?clients_categories=$matches[1]&clients=$matches[2]';
                // Add a rule of the sort "client-category-slug/parent-client-slug/client-slug"
                $additional_rules[ "({$term->slug})/([^/]*)/([^/]*)" ] = 'index.php?clients_categories=$matches[1]&clients=$matches[3]';
            }
            $wp_rewrite->rules = array_merge( $additional_rules, $wp_rewrite->rules );
        }
        $did_rules = true;
    }
}
add_action( 'generate_rewrite_rules', 'add_client_rewrite_rules' );

As for a begining, I assumed that the slug of your custom post type is clients and the slug of your custom taxonomy is clients_cateogires – if any of those are incorrect, please find and replace those occurances in my code.

In the arguments for register_post_type() for your clients post type, you have to set rewrite to:

'rewrite' => array( 'slug' => 'clients', 'with_front' => false ),

Also in the arguments for register_taxonomy() you have to set rewrite to(any other slug will work as well):

'rewrite' => array( 'slug' => 'client-category', 'with_front' => false ),

So, the first function hooks to the single_template and clients_template filter hooks. You can read in detail about this hook here, but basically this hook allows you to change the template that is being loaded when a singular(page, post, custom post type post) post is being displayed. We hook to this function, so that we can load the appropriate template when a clients post is being loaded.

First we check that the post type of the current post is actually the target one(clients).

Then we get all categories associated with the current client. If there are no categories, we let WordPress load the default template.

If there are categories we first check to see if the queried URL contains a clients_categories slug. Initially I thought that WP would add the slug of the clients_categories that is part of the URL, but it seems that it doesn’t. Instead I found that in the $matched_query property of the $wp object. If it matches a simple regular expression, we extract the slug and check it against the slugs of the categories that the current client is associated with. If one of them matches, we try to find a template single-clients-{$slug}.php.

If we couldn’t find the searched term, or the proper template for this term, we get the first category for this client and try to find the template for it(using it’s slug).

In the end if we have found an appropriate template, we set the $single_template variable to it, otherwise we preserver it’s original value.


Now the second part replaces client/ from the clients permalink with the appropriate clients_categories slug for this client. Note that it also checks to see if we’re on a term from the clients_categories taxonomy – if we are, it will use the slug of the current term. Otherwise it will add the slug of the first term that it finds.


The third part(clients_categories_custom_permalink()) fixes the URL’s for the taxonomy archives – this way the “Web” category will be at http://example.com/web/.


The fourth part(add_client_rewrite_rules()) adds some custom rewrite rules – note, that for these to take effect, you have to go to Settings > Permalinks – so that WordPress knows what it should display when it encounters an URL like http://example.com/web/ or http://example.com/web/client-slug/.


Note that if you have a page with a slug that matches one of the slugs for your Clients Categories – you will most-likely see the taxonomy archive, instead of the page.

Leave a Comment