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.

Tutorial

Receive New Posts by Email

Comments

  1. 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?

    1. 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.

  2. 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

      1. 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.

  3. 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;

    }

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

Leave a comment