Modular Themes, Part 1: Why?

April 2010

Have you ever wanted to change a class in the loop?You’re likely going to have to edit upwards of five files. The worst part? Most of the code you’re editing is identical.

A note, before we get started:
If you already know about get_template_part(), you’ll probably want to skip down to the section entitled “Using get_template_part() to Simulate the Template Hierarchy”.

If you’ve seen my session at WordCamp Ireland, this will seem very familiar.

What causes repetitive code?

As theme developers, our needs and our development pattern form a paradox: We need the ability to completely change any page, but most of the time we only change small sections and want to keep the rest in sync.

The problem lies in how our code is organized. In the current system, each file represents a page within the template hierarchy. However, we often don’t want to change the entire page, but a small section of code. As a result, we copy large chunks of code to a new page.

The system does not provide us with a viable way to manage code blocks below the page-level scope. At the moment, we have three alternatives:

  1. Copy code, and create a new page.
  2. Use template conditionals such as is_archive().
  3. Encapsulate your code in a separate function.

While template conditionals have their place, they must be used in moderation. Otherwise, files quickly become bloated and unreadable. Functions are a little trickier: at first glance, they seem to be an ideal solution. Combined with template conditionals, they can produce the desired results without swelling template files. The problem? PHP functions cannot be overridden.

When you define a function in a parent theme, it cannot be changed in a child theme. To tweak the code found in parent_page_title(), a child theme will have to copy parent_page_title() into a new function, child_page_title(), and then copy every theme file that calls parent_page_title() and replace it with child_page_title(). That’s like the Grand Poobah of repetitive code.

The First Step: get_template_part()

Okay, so if you’re using the latest beta (or trunk, if you’re awesome), the current system has a treat for you. In WP 3.0, theme devs get new function for their toolbox: get_template_part(). In the words of the documentation:

get_template_part() “makes it easy for a theme to reuse sections of code in an easy to overload way for child themes.”

Sounds good, I’ll take two. This function is just what we needed—now we can call get_template_part('page-title') and WordPress will load page-title.php, and even check the child theme first.

get_template_part() has an optional second argument that will first look for a specific version of the template. So if we were to call get_template_part('page-title', 'special'), WordPress would first check for page-title-special.php, and if that’s not found, look for page-title.php.

Using get_template_part() to Simulate the Template Hierarchy

As the new default theme on the block, Twenty Ten sets a good example for theme developers everywhere. Let’s look at how Twenty Ten uses get_template_part():

As you can tell, Twenty Ten uses get_template_part() to load the loop. While doing so, Twenty Ten loads custom versions of the loop in a fashion identical to the template hierarchy. Note that Twenty Ten only defines loop.php; it anticipates that child themes will use the more specific files.

I think that taking the template hierarchy into consideration when creating template parts is a fantastic idea, but I have a few problems with this implementation:

  1. The burden is on the user to implement the template hierarchy. What if the theme only defines archive.php? If the author wants to include a category-specific template part, they’re back to template conditionals or copy/paste.
  2. There is no naming convention. Yes, Twenty Ten follows the template hierarchy’s naming conventions, but what if a theme references “cats” instead of “category”, or  ”archives” instead of “archive”? There is no way to know without inspecting the theme files.
  3. Only part of the template hierarchy is implemented. In a Twenty Ten child theme, what if a user defines loop-date.php or loop-category-news.php? Their code will never be called, and again, they won’t know why until they read through the theme files. Developers who aren’t familiar with PHP will likely end up bewildered. Silently failing is bad.

This begs the question:

Instead of simulating parts of the template hierarchy, what if we just applied the template hierarchy to template parts?

Modular Themes

Well, we can. I’ve proposed a function get_template_module() that loads the best match in the template hierarchy from a given folder. While get_template_part() still has its place, get_template_module() is ideal in situations where a theme developer wants to vary a block of code depending on the template hierarchy.

Using Twenty Ten as an example, we would move loop.php to loop/index.php, and replace all calls to get_template_part() with get_template_module('loop').

When you’re viewing the “News” category, with category-id 4, get_template_module('loop') checks for the following and loads the first match:

loop/category-news.php → loop/category-4.php → loop/category.php → loop/archive.php → loop/index.php

Not Just for the Loop

get_template_module() has a whole bunch of uses, including potentially changing the way we organize our themes (which, incidentally, is the subject of my next post). Want to see get_template_module() in core? Read the trac ticket.

Modular Themes:
Trac Ticket | Part 1: Why? | Part 2: Theme Organization | Download | Performance