Adding Context to Template Parts

Posted on

By Michael Fields

One thing that I learned while creating the Ghostbird theme was that is is really important to take advantage of as many templates as possible. I have always said that “If I ever released a theme, it would only have one template file”. This is how Ghostbird was structured for about 90% of the time spent developing it. If you look at version 0.9.1, the only template file you will find is index.php. It was not until a very late stage that I saw the error of my ways and decided to add in other template files like search.php, 404.php and many more.

Once these files were in place I started to look very closely at them to see if there was anything that I could do to make child theming easier for users. I discovered that adding context to all template parts is a very easy thing to do and can allow child themes to modify small sections of a template.

search.php

This is one of the first template files that I added. I wanted to be sure to create a unique presentation of search results. I have always been bothered by themes whose search pages look exactly like their blog views. It was one of my goals to make my theme’s search results appear as if they were generated by a search engine. If you want to see what I came up with, you can search my site and check it out.

Here is a very simplified version of a search.php template:

get_header();
get_template_part( 'loop' );
get_sidebar();
get_footer();

It is relatively easy to override this template in a child theme by copying this file from the parent into the child and then modifying the contents.

But what if the user only needs to change something in the loop? What if they want to define a custom sidebar for use only in search views? It kinda seems silly to have to copy the entire template for such a task.

What I discovered is that all of the functions listed above can take a $name parameter.

get_header( $name );
get_template_part( 'loop', $name );
get_sidebar( $name );
get_footer( $name );

For my theme’s search.php I used the string search for the $name parameter to give the appropriate context that the function is being used in.

get_header( 'search' );
get_template_part( 'loop', 'search' );
get_sidebar( 'search' );
get_footer( 'search' );

Now that the context has been set for each template part function, it is easy to create any of the following files in the child theme to override a small section of the actual template:

  1. header-search.php
  2. loop-search.php
  3. sidebar-search.php
  4. footer-search.php

Once I discovered this trick, I wanted to apply it to all of my templates!

single.php

I’ve read many places before that single.php is the template used to display a single post. While this statement is not technically false, it doesn’t really tell the whole story. This file is actually responsible for displaying a single post of any public post_type (except for pages) that does not already have a designated template file. In other words, single.php is a “fallback” template for just about any query that asks for a single post.

Since single.php can be used to display any post_type, I decided that the $name or context parameter should use the post_type of the post instead of the name of the template file. My single.php looks something like this:

/*
 * Capture the name of the queried post_type.
 *
 * Note: It's a good practice to prefix variables
 * with something that will not conflict with other
 * extensions. That is why I used $ghostbird_post_type
 * instead of $post_type here.
 */
$ghostbird_post_type = get_post_type();

/* Use the post_type as the context for each template part. */
get_header( $ghostbird_post_type );
get_template_part( 'loop', $ghostbird_post_type );
get_sidebar( $ghostbird_post_type );
get_footer( $ghostbird_post_type );

This setup allows for a great level of flexibility in child themes. Probably the most useful thing that can be done is to create a unique loop template for each registered post_type:

  1. loop-post.php
  2. loop-person.php
  3. loop-product.php
  4. loop-bookmark.php

Had the context been given as “single” (like I have seen many themes do) the only solution here would be to create a file named loop-single.php and then write conditional logic within the loop to render a different format depending on the post’s post_type.

In Conclusion

Adding context to template parts allows a user to affect only small parts of any given template. Some may argue that setting context in get_header() or get_footer() is only helpful is certain situations or “edge cases” and I totally agree! I feel that it important to allow for the greatest flexibility possible for all users. When all is said and done, this may only really help out a very small percentage of your users, but it is very easy to implement and makes extending your theme easier for those who take advantage of this functionality.

8 Comments Comments are closed

  1. curtismchale April 21, 2011 at 2:22 pm

    Great thought on adding context to the template files. From reading it seems that you didn’t actually add header-search.php since it will default to header.php but this isn’t actually stated.

    Am I correct in that assumption or did you add template files for every context? If you did why did you knowing that they would default to the standard templates?

  2. Michael Fields April 21, 2011 at 2:27 pm

    Thanks Curtis! No, I did not create the individual template parts in my theme. I’m basically offering the possibility of these files being included in a child theme. However, I did include loop-search.php in the actual theme. It can still be overridden in the child, but will be used instead of loop.php in the parent for search views.

  3. Thomas Scholz April 23, 2011 at 2:17 am

    While this approach is very comfortable for child theme developers, it has a price: one additional file_exists() check for each named template, two in child themes. Compared to WordPress’ total operating time it may not matter. But still …
    I think, I have to test it. :)

  4. Michael Fields April 24, 2011 at 9:19 am

    I think it’s a small price to pay for the flexibility offered … but understand that there are people who will not agree with me. I think that installing a caching plugin will completely eliminate the extra processing power used to locate all of the templates.

  5. Rachel Nabors April 29, 2011 at 9:15 pm

    I like this approach very much and may have to include it in my core comics theme. So long as it doesn’t give too much of a performance hit from behind a cache, it sounds worth it.

  6. Kaiser June 23, 2011 at 6:19 pm

    I understand why you take this approach, but it has one large drawback: You really got a lot of files without any order. Afaik: You can use folders with get_template_part( ‘loops-folder/loop’, ‘post’ );

  7. Michael Fields June 23, 2011 at 6:30 pm

    Hi Kaiser,
    I actually do not see that as a drawback in most cases. It really depends on the size of the project though. Most that I work on do not demand a directory structure as you are proposing and large directories do not bother me. I know that others do not feel this way though :) Was not aware that you could declare a directory structure. Neat!

Fork me on GitHub