After reading through this guide, you will:
- Understand the Roots Wrapper and recognize how it extends and complements the WordPress template hierarchy.
- Know what is meant by the DRY Principle, why being DRY bests being WET, and see how most WordPress themes are WET.
- Be able to filter the Roots Wrapper and create a new base template for a Custom Post Type.
WordPress is pretty smart. Every time you load up a request it will search for the most relevant template available in your theme and load it. This is the Template Hierarchy in action and it enables us to easily customize the appearance of our sites.
Want to customize a specific page named "About"? Just copy page.php
, to page-about.php
and edit away to your heart's content. You only need to look at the success of WordPress to realize that this system works because of its simplicity and accessibility. But it doesn't work perfectly.
To prevent each new template from having a duplicate header, footer and sidebar, WordPress encourages you to separate that code into other templates and include them with the get_header()
, get_footer()
and get_sidebar()
functions (all of which are based on get_template_part). Whilst this makes your code more manageable, with only one edit needed to implement a change across all templates, it still duplicates code that simply doesn't need to be duplicated; the code which calls each of those templates.
In your typical wrapperless theme, every page template will look something like the following;
<?php get_header(); ?>
<div class="wrap">
<div class="content">
<?php // Our page specific markup and loop goes here ?>
</div>
<?php get_sidebar(); ?>
</div>
<?php get_footer(); ?>
Even though we know that every template will take this base format and render the header, footer, sidebar calls each time, we still need to continuously repeat the code to keep WordPress happy; it's laborious and unnecessary.
DRY simply means Don't Repeat Yourself and conforming to the DRY Principle means:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. [1]
So whilst we have a base format for our pages, this "knowledge" is written countless times, spread across numerous files and has no authoritative representation. This is the opposite of DRY code and it's usually described as being WET, meaning that you Write Everything Twice.
As you can see from the wrapperless template, WordPress goes beyond writing everything just twice, which is why I prefer the term CRY or Continously Repeating Yourself [2]. Whatever you want to call it, it wastes your time when creating the code, when reading the code and when editing the code; it's a lose-lose-lose situation (plus repetition is only fun in rhetoric) but it's easy enough to avoid.
##Wrap Up and Stay DRY##
The goal of a theme wrapper [3] is to remove any repeated markup from individual templates and put it into a single file. This file, base.php
becomes the single, unambiguous, authoritative representation of knowledge (i.e. the base format code). By doing this we can put the focus entirely on the page specific markup and loop, simplifying our templates to look like this:
<?php // Our page specific markup and loop goes here ?>
It's neat. It's tidy. You never need to make calls to get_header()
, get_footer()
or get_sidebar()
again. You can also refactor the base format of your site by editing base.php
. But best of all, it takes less than 50 lines of code to do so.
This is done using the standard WordPress Template Hierarchy, which, as I mentioned before, selects the most relevant template as our starting point. Once this template has been chosen, but before it's loaded, WordPress runs the template_include($template)
filter.
We use this filter to run our wrap
function that saves the $main_template
path and $base
as static variables in our Roots_Wrapping
class;
<?php
class Roots_Wrapping {
// Stores the full path to the main template file
static $main_template;
// Stores the base name of the template file; e.g. 'page' for 'page.php' etc.
static $base;
static function wrap($main) {
self::$main_template = $main;
self::$base = basename(self::$main_template, '.php');
if (self::$base === 'index') {
self::$base = false;
}
return new Roots_Wrapping();
}
}
add_filter('template_include', array('Roots_Wrapping', 'wrap'), 99);
?>
The wrap
function also checks to see if the $base
is index
(in which case we know to use the default base.php
) and finally returns a new instance of Roots_Wrapping
.
During the construction of our new Roots_Wrapping
instance, we set a variable for the $slug
(which defaults to base) and create a new $templates
array with the fallback template base.php
as the first item.
We then check to see if the $base
exists (i.e. confirming we're not starting on index.php
) and shift a more specific template to the front of the $templates
array,
<?php
public function __construct($template = 'base.php') {
$this->slug = basename($template, '.php');
$this->templates = array($template);
if (self::$base) {
$str = substr($template, 0, -4);
array_unshift($this->templates, sprintf($str . '-%s.php', self::$base));
}
}
?>
We then use the magic __toString()
function to apply a filter to our final $templates array, before returning the full path to the most specific existing base template via the inbuilt WordPress function locate_template
.
<?php
public function __toString() {
$this->templates = apply_filters('roots_wrap_' . $this->slug, $this->templates);
return locate_template($this->templates);
}
?>
All this is done before any content is served; so at this point WordPress has given us a starting point, which we've changed to a base file, thus extending the template hierarchy by one step. We also fire off a filter, so that the Roots_Wrapper
can be easily overridden or added to.
Once the theme wrapper has used locate_template
to select the appropriate base file, it will then start serving the base markup (grabbing the header, footer and sidebar code as it goes along). The content from the WordPress starting point i.e. $main_template
will be loaded by the line:
<?php include roots_template_path(); ?>
This is a helper function that returns and loads the $main_template
we saved earlier. You could also manually include the template from the base-*.php
or just have all the code in that one file alone. See the footnotes for caveats [4].
Effectively we've started and ended with the standard WordPress Template Hierarchy, but grabbed the base format from the appropriate base file in between. The markup of our content is wrapped between our base markup, the cycle completes and the theme wrapper's job is now done. In fact, because the theme wrapper starts and ends with the output from the standard Template Hierarchy, the vast majority of issues can be resolved just by looking through and understanding the Template Hierarchy Codex Page.
Let's say you wanted your base markup to change depending on what CPT WordPress was using. With the Roots Wrapper you just need to add the following to your lib/custom.php
file (or anywhere else more appropriate):
<?php
add_filter('roots_wrap_base', 'roots_wrap_base_cpts'); // Add our function to the roots_wrap_base filter
function roots_wrap_base_cpts($templates) {
$cpt = get_post_type(); // Get the current post type
if ($cpt) {
array_unshift($templates, 'base-' . $cpt . '.php'); // Shift the template to the front of the array
}
return $templates; // Return our modified array with base-$cpt.php at the front of the queue
}
?>
Now when the base markup is loaded via return locate_template($this->templates);
it will load our CPT template if it exists. When a CPT doesn't have its own template WordPress will simply revert to using our standard base.php
file.
Comments, as always, are very welcome. Also don't forget to subscribe for more Roots related goodies!
- [1]: Coined in [The Pragmatic Programmer](http://pragprog.com/the-pragmatic-programmer) by Andy Hunt and Dave Thomas.
- [2]: Cry => Tears => Wet. Coined by Nick Fox, in this article, it won't catch on, but sometimes it's nice to be a footnote.
- [3]: The Roots Wrapper is based on [this post](http://scribu.net/wordpress/theme-wrappers.html) by Scribu. Scribu's code is in the public domain but credit where it's due. The Roots_Wrapping class has been modified and is MIT licensed.
- [4]: Please do not put all your code in a base-*.php unless you're certain you're being clever; if you only think you're being clever then it's not yet clever enough.