Hacking the WordPress Template Hierarchy

Chalkboard

Today we have a very esteemed guest author: Josh Pollock! Josh is the co-founder of CalderaWP, which authors top-quality WordPress plugins. They’ve just released a new plugin: Easy Queries, an awesome new visual editor for WordPress queries.

Josh Pollock | WPShoutLearn more at CalderaWP, and get a 20% discount on Easy Queries when you use the code SHOUT! at checkout.

The WordPress template hierarchy is the system that defines which PHP template file your theme (or child theme) will use to display a given content type on your WordPress site. As such, it allows you to customize how your theme shows different content, based on context. Combined with a strong set of template tags, most of the time it covers every situation you need.

Of course, WordPress can’t handle every requirement for every site, so there’s also a set of hooks that you can use to customize how the template hierarchy behaves. While these hooks are generally not used in themes for general release, they are very helpful when building a theme for specific site, or in plugin development.

If you are not familiar with the template hierarchy yet, you should familiarize yourself with it before reading this article.

In this article, I am going to walk you through three hooks—the second of which comes in many flavors—that you can use to customize the behaviour of WordPress’ template loader. This article will go in the same order that WordPress uses to execute these hooks as it works to determine which template file to load from the current theme.

Bypassing The Template Loader with template_redirect

The first hook that fires is template_redirect. This is an action that runs before the template is determined. While there is a history of using this to set which template to use, that is very bad practice and had a lot of negative side effects. Instead, template_redirect should be used in cases when you need to prevent loading a template at all.

This action’s most important purpose is that it is a way to respond to a request without loading a template from the theme at all. For example, the WordPress REST API hooks in at template_redirect, creates a JSON response, returns it, and ends the session—all without ever hitting the template loader, which is not needed.

This is a useful strategy if, for example, you are creating another type of API, such as a custom API for processing front-end AJAX requests, or for triggering a regular event using an external cron service, or anything else that doesn’t require the use of your theme. Just make sure to use the function exit() to end the session, to prevent WordPress from continuing to the template loader.

One excellent use for template_redirect is to lock out non-logged in users from viewing a specific page. For example:

add_action( 'template_redirect', function() {
	if ( ! is_user_logged_in() && is_page( 'members-only-content' ) ) {
		wp_redirect( wp_login_url() );
		exit();
	}
});

Notice that in the example code above, I was able to use the function is_page()—since the main query is actually run right before template_redirect fires. This is part of why tempalte_redirect is so useful. If we hook in any earlier it is very hard to know the context of the current request.

Filtering Individual Parts Of The Template Hierarchy

Once template_redirect is passed, WordPress loads up the actual template hierarchy system, to determine which type of template it should use. For every template file it could attempt to load, there is a filter. This filter uses a dynamically generated name based on the file that it should load.

This means that when WordPress determines that it needs to load single-post.php, before it does so, it fires the filter single-post_template. This allows you to change what template is loaded for a specific type of file.

What’s really cool, is that WordPress fires these filters even if the relevant file doesn’t exist in the current theme. So single-post_template fires before WordPress falls back to the next template it would try and load, in this case single.php.

Here’s an example of how we can use this filter, in a plugin, to provide a default template for the single post view of a post type called hats. Note that this only takes effect if WordPress didn’t already find a single-hats.php template in the current theme or child theme:

add_filter( 'single-hats_template', function( $template ) [
	if ( ! $template ) {
		$template = dirname( __FILE__ ) . '/templates/default-hats-single.php';
	}
	
	return $template;
});

Notice that I didn’t use is_singular( 'hats' ) here to check that WordPress is on a single post of the hats custom post type. That check isn’t needed, since this filter will only ever run in that context.

The Catch-All Filter: template_include

Once WordPress has run the whole template hierarchy system, it runs one more filter before the template is actually loaded. That filter is called template_include and its result is passed directly to include() to actually include the template.

This filter is commonly used in place of the dynamically named filters that I discussed in the last section, probably because those filters are poorly documented. As a result, developers often use template_include, with a string comparison on the template being called, to conditionally intercept specific templates. That works, but is not optimal.

The reason why it’s better to use the dynamically-named filters is that the template hierarchy runs a series of foreach loops to see if the current template it is trying to load exits. The sooner you intercept this process, the sooner it finishes and the faster the template loads.

Since template_include runs last, it can be used as a catch-all. I used it recently, in a WordPress-powered app I was prototyping to make it so only one template file would ever be used. For apps, or single-page sites, this is a useful strategy.

A cool thing that you can do with template_include is create a “grandchild theme.” While this isn’t a concept that exists in WordPress, you can create a child theme for a child theme, using a plugin to serve as the grandchild theme. In the plugin’s main file, you would add a function like this:

add_filter( 'template_include', function( $template ) {
  $path = explode('/', $template );
  $template_chosen = end( $path );
  $grandchild_template = dirname( __FILE__ ) . '/' . $template_chosen;
  if ( file_exists( $grandchild_template  ) ) {
     	$template = $grandchild_template;
  }

  return $template;

});

The callback function hooked to template_include determines the name of the template WordPress is about to load, and uses it to build a path to the template in the plugin. It then checks if that file actually exists. If it does, then it tells WordPress to load that file. If not, then nothing changes, and the same fallbacks—to child theme, then parent theme—take effect.

Go Forth And Customize

If you’re creating themes to be released to the public, you need to respect the template hierarchy. On the other hand, when you’re working on a theme or child theme for a specific site, these hooks are very useful for handling client requirements that no WordPress default can accommodate. In addition, a knowledge of these filters becomes very handy for plugin developers: they allow you to create templates for displaying content from your plugin.

I hope this article helped you not only learn how to customize the WordPress template hierarchy, but encourages you to dive into WordPress core, find these filters, and see how the template hierarchy works. It’s a neat system, and understanding how it works will benefit your WordPress problem solving skills enormously.

Image credit: Dan Zen


15 Responses

Comments

  • Interesting. I have to think think about this 🙂

    What I’ve been doing is to eliminate all standard theme root templates.php except for the index.php. Then within that I use conditional tags and any other biz rules to define which get_template_part() is needed (e.g., 404), and then the partials within that have rules and so on.

    My views are more like Russian nesting dolls, with a define-a-rule bottleneck each time a parent temple part calls a child tempate part. Sounds weird (because it’s not at all The WordPress Way, it’s The ezWay lol) but it’s actually very simple and very effiective.

    Not only does this allow for making more sophisticated biz rules easier to implement but my views / partials are easier to reuse.

    Note: I’m kinda cutting some corners in my explanation but the get template part / Russian nesting doll analogy is pretty accurate.

    • Doug Cassidy says:

      I used to do exactly that, too. For the same reason “it’s The ezWay” lol. But also, cuz I was a wp noob 😉 It seemed like the best way, at the time. One of the biggest problems is that if you do child themes, you’re just setting yourself up for more work than you need to. Also, it makes it a little harder to be able to choose themes from the edit page (you have to roll your own chooser post_meta and then look for it in your big list of ‘ifs’ in the russian dolls.)

      • Thanks Doug. But I don’t think we’re talking about the same approach. Only because I find child themes easier, not hard. And I find, if I code the partials correctly (that is, with re-usability in mind) I save time by reusing code / views.

        What I do is setup some baseline / boilerplate parent and then use biz rules in the child to override those in the parent. I guess I should mention my biz rules are in my model so the actually traditional WP theme is just structure. I even have my CSS classes in the model so it’s super easy to make CSS changes without actually changing the theme.

        So for any theme, parent or child, a template part looks to the model for “instruction”. And part of that could be “don’t use this sub template part, use this one instead.”

        In short, you get smaller more granular modules with more control of them because what to do is close to 100% separate from presentation.

        So if I can define a rule (e.g., When post_id = X) I can make special magic happen.

      • p.s. I’m not at all concerned about public themes. This is simply an approach for bespoke work. Not that it has to be limited to that, I’ve just never said to myself “What if this were a public / commercial theme?”

        I’ve done what I’ve done because of limits in the traditional approach, as well as having to deal with one too many sprawling WP “applications” that lacked granular structure and proper definition of layers.

  • Follow up question, please.

    How do you – and pardon me because I sense I should know this – manipulate the list of templates that shows on (say) Add New / Edit Post?

    And also, how do you know which a given post has been assigned? Is it simple a post meta “property” of the post?

    I ask because, given my previous comment, I might be interested in using the custom post template select, without actually having to have those .php files. That is, if that list for the select is filterable and options can be added, then – for the theme’ing approach I use – the actually .php files aren’t really necessary.

    Thanks in advance.

Pingbacks