Codeable info

Loading Custom WordPress Theme Templates Based on Query

Posted on by in WordPress Tutorials

For a WordPress theme to work, it really needs one template: index.php. But, using that file for all types of content is not really a good idea, since it limits the way content is displayed. Because of that, WordPress has a template loader, and this loader uses query (based on query string) that generates content for display to determine what template to load. Basic template hierarchy supported by WordPress can be found here: http://codex.wordpress.org/Template_Hierarchy

But, in some cases, this is not good enough, and you will need finer templates separation to avoid writing complex conditionals for displaying the content. To achieve this, you to hook into the templates loader, and change the list of potential names for templates. Each template type has its own filter for this purpose. Full lists of these filters can be found in the codex URL above, or you can check out template-loader.php and template.php files in WordPress wp-includes folder.

How Template Loader Works?

Template loader is basically the file that uses conditional functions to determine what type of content is requested and then find the template in the currently active theme to load. Each content type has own template function, and each of this functions is creating list of supported templates, and then searches for the available template based on the hierarchy.

But, when the template is found, filter for each content type allows you to hook into and change the template that is loaded. Examples in this article will use that approach and they will, just like WordPress does, create list of templates and then search for available ones.

Expanding Single Post Templates

Default templates hierarchy for single post contains these templates:

  • single-{$post_type}.php
  • single.php
  • index.php

This means, that WP will first look for single-{$post_type}.php template (if you have post type called movie, this template will be single-movie.php), if that one is missing, then it will look or single.php, and if that one is missing, it will load index.php.

For this example, we want to add templates based on post ID and post slug, so that individual posts can have own templates, just like WordPress does with pages.

add_filter('single_template', 'd4p_expand_single_template');
 
function d4p_expand_single_template($template) {
   global $wp_query;
   $object = $wp_query->get_queried_object();
 
   $templates = array(
       'single-'.$object->ID.'.php',
       'single-'.$object->post_name.'.php',
       'single-'.$object->post_type.'.php',
       'single.php',
       'index.php');
 
   return locate_template($templates);
}

This code hooks into single_template filter. This filter sends the path to the template loaded by default. But, we will now create our own list of templates, and we are adding two new templates on top, and the list of templates is now this:

  • single-{$post_id}.php
  • single-{$post_slug}.php
  • single-{$post_type}.php
  • single.php
  • index.php

So, if you have post in the movie post type called: The Dark Knight, and it has ID 110, potential new templates for this post would be:

  • single-110.php
  • single-the-dark-knight.php

If you use one of these, only this one post will use them, and you can create very unique template for this one post.

Expanding Date Based Archives Templates

For dates archives, WordPress uses these templates:

  • date.php
  • archive.php
  • index.php

This means that all date based archives use only one unique template: date.php. This next example will show you how to add templates based on actual date.

add_filter('date_template', 'd4p_expand_date_template');
 
function d4p_expand_date_template($template) {
   global $wp_query;
 
   $templates = array();
 
   if (is_year()) {
       if (isset($wp_query->query['m'])) {
            $year = substr($wp_query->query['m'], 0, 4);
       } else {
            $year = $wp_query->query['year'];
       }
 
       $templates = array_merge($templates, array(
           'date-year-'.$year.'.php',
           'date-'.$year.'.php',
           'date-year.php'));
   }
 
   if (is_month()) {
       if (isset($wp_query->query['m'])) {
            $month = substr($wp_query->query['m'], 4, 2);
            $year = substr($wp_query->query['m'], 0, 4);
       } else {
            $month = $wp_query->query['monthnum'];
            $year = $wp_query->query['year'];
       }
 
       $templates = array_merge($templates, array(
           'date-month-'.$year.'-'.$month.'.php',
           'date-month-'.$month.'.php',
           'date-'.$year.'-'.$month.'.php',
           'date-'.$month.'.php',
           'date-month.php'));
   }
 
   if (is_day()) {
       if (isset($wp_query->query['m'])) {
            $day = substr($wp_query->query['m'], 6, 2);
            $month = substr($wp_query->query['m'], 4, 2);
            $year = substr($wp_query->query['m'], 0, 4);
       } else {
            $day = $wp_query->query['day'];
            $month = $wp_query->query['monthnum'];
            $year = $wp_query->query['year'];
       }
 
       $templates = array_merge($templates, array(
           'date-day-'.$year.'-'.$month.'-'.$day.'.php',
           'date-day-'.$day.'.php',
           'date-'.$year.'-'.$month.'-'.$day.'.php',
           'date-day.php'));
   }
 
   array_push($templates, 'date.php', 'archive.php', 'index.php');
   return locate_template($templates);
}

This template uses one small trick. When the permalinks are active, WP_Query object will have all date parts parsed into query variables. But, if you use query string based URL, this will not happen. That is why we need to check if non-permalinks query variable m is present or not.

First part is to check if the date is a year. If that is the case, we get the which year was requested, and then we add templates for it. Similar method is used for month and day. For the last two we are adding templates combinations and variations for same thing, and you can remove ones you don’t actually need. And at the end we are adding standard templates back to the list, in case none of the custom ones is found.

With all this, we are adding following templates:

  • date-year-{$year}.php
  • date-{$year}.php
  • date-year.php
  • date-month-{$year}-{$month}.php
  • date-month-{$month}.php
  • date-{$year}-{$month}.php
  • date-{$month}.php
  • date-month.php
  • date-day-{$year}-{$month}-{$day}.php
  • date-day-{$day}.php
  • date-{$year}-{$month}-{$day}.php
  • date-day.php

Some of them will use year, month and day. So, you can have specific template file for archives for year 2011: date-year-2011.php or date-2011.php, and you can have archive for march 2012: date-month-2012-03.php and date-2012-03.php and so on.

Conclusion

As you can see, adding custom templates is not that hard to do, and it will give you a great tool to make more content related templates with using less conditionals and making messy standard templates. You can use similar method as shown here to add many more variations for any type of template.

If you want these examples in the form of the plugin, you can download them from the link below:

Link: d4p wphub customtemplates.1.0.0

)
Codeable info

Comments (2)

Comment by thiet ke website tai tphcm says:

I have two link
category/recommend
category/buy-a-franchise

How to change template by URL ?

Comment by Mohsin says:

Never had a look at this one before, this is an awesome way to make exceptions for a particular ID or slug!

Codeable info