Reusing Template Files

WordPress’ built-in template hierarchy is very useful for reusing template files on standard websites. If you have something you want used for all category archives, use category.php. If you want a template file to be used on all archives (category, tag, search…), use archive.php.

But when you’re building websites with complex content structures, you’ll often need to build a very specific template hierarchy of your own, or else duplicate a lot of code.

I’m building a site right now with four custom post types, and each post type has 2-4 taxonomies. Take a look at the screenshot above. The ‘recipe’ post type has two taxonomies: ‘name-range’ which allows the client to break down the list of recipes by name (ex: A-D, E-G…), and ‘base’ (dropdown says category), which is a shared taxonomy with another post type that specifies the alcoholic base of the cocktail.

I want a single template file that is used in all of these situations:

  • Recipe post type archive
  • Name Range taxonomy archive
  • Base taxonomy archive (if post type is also ‘recipe’)

For this, I’ll use the template_include filter. This filter runs right after WordPress determines what template it thinks it should use, but before that template is actually loaded. This is where you can hop in and specify your own template hierarchy.

Without this technique, I’d need to put the same code in archive-recipe.php, taxononomy-name-range.php, and taxonomy-base.php (with some extra code in here to check for the current post type).

<?php
/**
* Template Chooser
* Use CPT archive templates for taxonomies
* @author Bill Erickson
* @link http://www.billerickson.net/code/use-same-template-for-taxonomy-and-cpt-archive/
*
* @param string, default template path
* @return string, modified template path
*
*/
function be_template_chooser( $template ) {
if( is_post_type_archive( 'recipe' ) || is_tax( 'name-range' ) || ( is_tax( 'base' ) && 'recipe' == get_post_type() ) )
$template = get_query_template( 'archive-recipe' );
return $template;
}
add_filter( 'template_include', 'be_template_chooser' );
view raw gistfile1.aw hosted with ❤ by GitHub

The code above says “if this is the post type archive for ‘recipe’, the taxonomy archive ‘name-range’, or the taxonomy archive ‘base’ AND post type ‘recipe’, then use the template file archive-recipe.php.”

I also like to generalize this section checker into its own function so I can use it elsewhere. You might want to add a body class called ‘section-recipe’ if any of these templates are in use. Here’s how:

<?php
/**
* Return Archive Section
* @author Bill Erickson
* @link http://www.billerickson.net/code/helper-function-for-template-include-and-body-class/
*
* @param null
* @return string
*/
function be_return_archive_section() {
if( is_post_type_archive( 'lifestyle' ) || is_tax( 'lifestyle-section' ) || is_tax( 'lifestyle-type' ) || is_tax( 'lifestyle-series' ) )
return 'lifestyle';
if( is_post_type_archive( 'recipe' ) || is_tax( 'name-range' ) || ( is_tax( 'base' ) && 'recipe' == get_post_type() ) )
return 'recipe';
if( is_post_type_archive( 'brand' ) || ( is_tax( 'base' ) && 'brand' == get_post_type() ) )
return 'brand';
if( is_post_type_archive( 'experience' ) )
return 'experience';
return false;
}
add_filter( 'template_include', 'be_template_chooser' );
/**
* Template Chooser
* Use CPT archive templates for taxonomies
* @author Bill Erickson
* @link http://www.billerickson.net/code/use-same-template-for-taxonomy-and-cpt-archive/
*
* @param string, default template path
* @return string, modified template path
*
*/
function be_template_chooser( $template ) {
if ( be_return_archive_section() )
$template = get_query_template( 'archive-' . be_return_archive_section() );
return $template;
}
add_filter( 'body_class', 'be_section_body_classes' );
/**
* Section Body Classes
* @author Bill Erickson
*
* @param array $classes
* @return array
*/
function be_section_body_classes( $classes ) {
if( be_return_archive_section() )
$classes[] = 'section-' . be_return_archive_section();
return $classes;
}
view raw gistfile1.aw hosted with ❤ by GitHub

First I’m creating a function that returns the current section if there’s a match, and ‘false’ if there isn’t.

Then I have my template redirect function. If the current page is in a section, it loads the template ‘archive-$section’ (ex: archive-recipe.php).

Then I have my body class filter. If the current page is in a section, it adds the body class ‘section-$section’ (ex: ‘section-recipe’ ).

If you add a new taxonomy, just edit the be_return_archive_section() function to specify the section it belongs in, and it will automatically use the correct template file and have the body class.

Bill Erickson

Bill Erickson is the co-founder and lead developer at CultivateWP, a WordPress agency focusing on high performance sites for web publishers.

About Me
Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk

Reader Interactions

Comments are closed. Continue the conversation with me on Twitter: @billerickson

Comments

  1. Devin Price says

    Incredibly useful tip. I’ve been including multiple files for custom taxonomies on portfolios even though they display out the same. Thanks for posting.

  2. GaryJ says

    Could you not put common code into another file, and then have a simple require in the template files that need to make use of them?

    • Bill Erickson says

      Yes, but then you’d still need to create three template files which all include a require statement. This is a much cleaner solution, assuming they are all the same template. If you only want to include a few functions, then yes, a require is best, or include the function in functions.php and just put the add_action() in the template file.

  3. Jon Schroeder says

    Bill, this is a fantastic post. I’ve used something quite similar (though not quite as clean as this) for quite some time.

    I wondered if you might have a method for doing this in a plugin – but also allowing a file of the same name in the theme directory to override your plugin template. That would allow a plugin built for, say, displaying testimonials, to have a default template at /plugin/templates/archive-testimonial.php, but allow for very simple overriding by just copying the appropriate template file to a theme directory and making the changes to it there at /theme/archive-testimonial.php

    • Bill Erickson says

      I haven’t tried it, but I assume you could simply check if file_exists() at a specific path in the theme. If it does, use that template file; if it doesn’t, use the one in the plugin. There might be a more advanced way to do it though. You might try digging in bbPress as I know it works that way.

      Edit: Here’s the code in bbPress.

      • Jon Schroeder says

        Wow, that’s pretty intense; but I guess they have a lot more use cases to consider. They even have a method in there (from first glance) for enqueuing styles and scripts directly from template files.

        For my needs, I’ve started with your code here, then made some changes to do exactly what I’m needing, which is to use a child theme file if there is one for archive-cpt.php, fall back to the plugin file (archive-cpt.php) if not, and if there’s no file in the plugin, then fall back to the default WordPress template hierarchy.

        https://gist.github.com/redblueconcepts/d6db3a31e1b836c04da1

        Thanks so much for posting this (years ago at this point, I guess!). This will make a number of plugins I use regularly much more flexible, as there’s now a standard, intuitive way to override a template file to do something different than the default layout, keeping the main functionality of the plugin intact.

  4. Jamie Mitchell says

    Hi Bill !

    I seem to be getting a syntax error with this now (never used to)

    Parse error: syntax error, unexpected ‘)’

    any ideas

    // Use same archive template for tax
    add_filter( ‘template_include’, ‘starter_template_chooser’ );
    function starter_template_chooser( $template ) {

    if( is_post_type_archive( ‘products’ ) || is_tax( ‘product-type’ ) && ‘products’ == get_post_type() ) )

    $template = get_query_template( ‘archive-products’ );

    return $template;

    }

    • Jamie Mitchell says

      ah !

      there was an extra ‘)’

      removed that and solved the problem

      looks like it’s in your code above too

      🙂

    • Bill Erickson says

      The code above doesn’t have an extra parenthesis. You’re missing the opening parenthesis before is_tax( 'product-type' )

      • Jamie Mitchell Design says

        Ha!

        Thanks Bill

        Seems i was staring at the screen way to long yesterday 🙂