Drupal Theming - A Basic Primer on Template.php and Template Files
When I was first starting to dive deep into the Drupal theming layer (beyond making a subtheme and changes the styles with CSS), I found the documentation to be rather scattered. Since I'm also impatient, I wanted one place where I could go to get a quick overarching picture of what the different files in a Drupal theme do, and how a themer can use those files to bend the theme to the designer's will.
First, basic theme anatomy
Many people subtheme Drupal's contributed themes since there are already so many great starter themes that are highly functional. Why build from scratch when others have already done much of the work for you? If you've subthemed before (read about how to create a subtheme here) you'll already be familiar with the basic anatomy of a subtheme. You have your .info file where you name your subtheme and tell your subtheme what you're using for your base theme. You can get pretty far by just doing this and then going in and tweaking the CSS/LESS/SASS.
To take theming to the next level, you'll want to understand how the template.php and template files (page.tpl.php, node.tpl.php etc) work. I've tried to break this down into a simple table that highlights the differences between template.php and template files (confusing nomenclature, yes).
template.php | Template files (*.tpl.php) | |
---|---|---|
When is it run? | Contains code that is run every time the template engine is run. | Only runs when needed/appropriate |
What is it for? | template.php is a collection of functions that assist in the theming of the site. It’s for all the conditional logic and data processing of the output - the place to redefine or override theme functions, or add variables that will be made available to the theme engine. | Template file(s) defines the framework of a page or region. They provide templates for various components that are made available to the page.tpl.php file, such as blocks, boxes, etc. |
What are the basics of the code? | PHP: This file must start with a PHP opening tag and typically does not end with a PHP closing tag. | HTML only with some basic PHP to use variables, but shouldn’t have complex logic. |
What does Drupal Core provide? | There are default baseline variables available to all template files. | Core template files. |
How is the file used in the theme? | There will be one template.php file for a theme, and this one file will define any needed variables. | There can be none or many template files for a theme, including (but not exclusively) page.tpl.php, block.tpl.php, node.tpl.php... |
What can you add or customize through this? | You can add new variables and template suggestions. | The layout of the specified page/node/region. |
I'll walk you through template files and template.php in more detail here. We'll first start with template files (*.tpl.php) since these are the easiest to read if you're not too familiar with PHP since they are mostly HTML.
Template Files (*.tpl.php) - More DETAILS
So now we understand that template.php and template files play distinct roles in our theme. We use the template.php for all of our conditional logic, so we keep the template files clean and looking (for the most part) like HTML. Template files, like page.tpl.php, node.tpl.php, block.tpl.php, comment.tpl.php, define the HTML for different parts of the website, and are named pretty logically. Node.tpl.php will apply to all nodes, block.tpl.php applies to all blocks, comment.tpl.php applies to all comments - you get the picture. One thing to not get confused about here - page.tpl.php is the template file that is applied to all pages for Drupal - it's not a specific template file for nodes of the content type 'page'.
Template Suggestions
If you want to customize the HTML for any of these regions or pages, you place the appropriate template file in the templates directory of your theme, and then customize. If you want a custom template file for the home page only (a common use case) you can create a page--front.tpl.php file. This is called a template suggestion. This will override the page.tpl.php layout only for the front page. Likewise, if you want to customize the layout of articles nodes, but no other content types, you create a node--article.tpl.php file and this will override node.tpl.php. You can get more and more specific, all based on how you name the template files. Read more on Drupal.org about template (theme hook) suggestions.
Variables
Drupal core already provides a some baseline variables that are available to our template files. In addition, each core template files provides additional variables that are available within that template file. You can see what variables are available to the template file by looking in the comments in the /** @file **/
at the top of the template file. Often the core template file only uses a small subset of available variables, so it's worth looking to see what else you can print out. For example, the node.tpl.php file provided by Bartik is pretty simple. I know, I said template files were mostly HTML, but this sure looks like a lot of PHP. If we break it down though, there isn't any complex logic or functions here. We mainly are using the PHP opening and closing tags to print out variables.
For example, let's print out the number of comments on a node above the comment form. We see that the variable $comment_count is "the number of comments attached to the node". Perfect. If we want to make the comment count an H2 to make it more noticeable, we will add the following to our node.tpl.php file:
<h2>Comments: <?php print $comment_count; ?></h2>
Refresh any page that displays a node and we'll see the following. (You may need to clear cache.)
What if we want something in a template file that isn't available as a variable? That's where template.php comes in - we can use template.php to create new variables and make them available to our template files.
Template.php - More Details
Most themers can work with template files without much understanding of PHP. To work with template.php, some more PHP knowledge is useful, but again it's pretty basic. We put all of our functions in template.php, such as preprocess and process functions, and here we can add new variables and template suggestions. Let's break down what each of these terms means.
Preprocess functions and variables
There are two kinds of preprocess functions you can create, theme_preprocess(), and theme_preprocess_hook(). To print out a list of available hooks on a given page, we create the simplest preprocess function (using the dpm()
function requires the Devel module):
function mytheme_preprocess(&$variables, $hook) {
dpm($hook, 'hooks');
}
Clear cache, refresh page and you'll get a long lists of hooks that are available. Here are just two:
These hooks are giving us a list of all of the possible template files we could use to customize this page or region. So now, if we want our preprocess function to be only called when the page hook is called, so it will only be called once on a page load, we modify our preprocess function as follows. We'll also print out the variables that are available.
function mytheme_preprocess_page(&$variables) {
dpm($variables, 'variables');
}
Now if we refresh the page, we will get a nicely formatted array of variables that are available for us to work with in our template.php.
Why do we care? Well, preprocess functions are great for doing things like creating new variables that we can pass to the variables array, and therefore make that variable available to our template files. Say we want to print the day of the week on our page. There currently isn't a variable for the day of the week, so we'll create one using the PHP date function. We'll then add that variable to the variables array (which we passed by reference in our function, so we can make changes).
function mytheme_preprocess_page(&$variables) {
$day_of_week = date('l');
$variables['day_of_week'] = $day_of_week;
dpm($variables, 'variables');
}
Refresh the page and we'll see at the bottom of the variables array we have a new variable, day_of_week.
Now we can use that variable in our page.tpl.php as follows:
<h2>Today is <?php print $day_of_week; ?></h2>
I've added this just above the title. Refresh any page and you should see:
In case my comment above about the hooks giving us a list of all of the possible template files didn't make sense, I'll clarify here. We used a preprocess_page function, calling the page theme hook. This means this new variable will only be available to the page.tpl.php template file. If we took the same code that we placed in the page.tpl.php to add the day of the week, and placed it in our node.tpl.php, our variable would not print out and we would get an error like:
Notice: Undefined variable: day_of_week in include() (line 82 of /projects/drupal-7.38/themes/mytheme/templates/node.tpl.php).
If we want to get even fancier and print the day of the week only to logged in users, we place the logic in our template.php, and keep the page.tpl.php simple. We know we have access to the logged_in variable by inspecting our variables array that we have dpm'd out on the page
And then we add the following code. If you refresh you'll see that logged in users can see the day of the week, but anonymous users get a snarky message.
function mytheme_preprocess_page(&$variables) {
$day_of_week = date('l');
if ($variables['logged_in']) {
$variables['day_of_week'] = 'Today is ' . $day_of_week;
} else {
$variables['day_of_week'] = 'You are not privy to that information';
}
}
Theme function overrides
We override theme functions if we want to change something in the layout of our theme that doesn't have a template file. There are lots of parts of the theme that don't have template files, if they are smaller bits of HTML, like breadcrumbs or the username. Here is another place the Devel and Theme developer modules comes in handy. In combo they are great for figuring out candidate template files and theme functions if we want to change something. For example let's say we want our breadcrumbs to be H3 elements, and we want to change the divider between the breadcrumb elements from '>>' to '++'. Using Theme Developer, click on the breadcrumbs to get the theme function. It's named something logical - theme_breadcrumb().
If you click on theme_breadcrumb() it will send you to the code for this function on Drupal.org:
function theme_breadcrumb($variables) {
$breadcrumb = $variables ['breadcrumb'];
if (!empty($breadcrumb)) {
// Provide a navigational heading to give context for breadcrumb links to
// screen-reader users. Make the heading invisible with .element-invisible.
$output = '<h2 class="element-invisible">' . t('You are here') . '</h2>';
$output .= '<div class="breadcrumb">' . implode(' » ', $breadcrumb) . '</div>';
return $output;
}
}
To make our changes, we put this function in our theme, rename it according to our theme name, and make the desired changes.
function mytheme_breadcrumb($variables) {
$breadcrumb = $variables ['breadcrumb'];
if (!empty($breadcrumb)) {
// Provide a navigational heading to give context for breadcrumb links to
// screen-reader users. Make the heading invisible with .element-invisible.
$output = '<h2 class="element-invisible">' . t('You are here') . '</h2>';
$output .= '<h2>' . implode(' ++ ', $breadcrumb) . '</h2>';
return $output;
}
}
Template suggestions
Another thing you can do in your template.php file is add template suggestions that aren't automatically available. There are already a lot of template suggestions available - let's take node.tpl.php as an example. If we wanted to change the layout of nodes of content type article, we would make a node--article.tpl.php file. If we wanted to change the layout of a specific node, we would make a node--[nid].tpl.php file. We can access the list of available template suggestions by dpm'ing out the variables in a hook_preprocess_node() function in our template.php as follows:
function mytheme_preprocess_node(&$variables) {
dpm($variables, 'variables');
}
Inspect the variables array for theme hook suggestions and we'll see two. Note that in the array they are listed with underscores, but when we make the template files, the underscores are replaced with dashes. Also, the order matters here - Drupal will look for the most specific template first, (at the bottom of the list), and then keep working its way up. It doesn't necessarily mean that these templates exist, but that Drupal will check to see if they do.
But say we didn't want to use either of these template files, and wanted to make a new template suggestion. For example, let's say we wanted to add a template file that would only display on the weekends, reminding people they shouldn't be inside on the interwebs, but outside doing something involving fresh air. We would add a template suggestion to the theme_hook_suggestions array as follows - note since we're adding to an array, we include an additional empty []. Also, we don't need to include '.tpl.php' in the name of our suggestion, and we're using underscores. For the actual template file itself, we will call it node--weekend.tpl.php.
function mytheme_preprocess_node(&$variables) {
$variables['theme_hook_suggestions'][] = 'node__weekend';
dpm($variables, 'variables');
}
Refresh, and check out your variables array - you'll see our new template suggestion is now listed. Also, since it is at the bottom of the list, it is the most specific file, so if that template suggestion and file exists, Drupal will use it.
Great, now we can make our node--weekend.tpl.php and put in an obnoxious message like this. Note that this will replace all content that would normally be displayed by node.tpl.php and only this message will be displayed.
The last thing we have to do is make sure that this template suggestion only exists on the weekends. Right now as our code was written, that template suggestion will always be there. We'll just do a quick check of the day of the week before we make the template suggestion.
function mytheme_preprocess_node(&$variables) {
$day_of_week = date('l');
$variables['day_of_week'] = $day_of_week;
if (($day_of_week == 'Saturday') || ($day_of_week == 'Sunday')) {
$variables['theme_hook_suggestions'][] = 'node__weekend';
}
dpm($variables, 'variables');
}
Now (as long as it's a weekend) if someone tries to view a node they will just get this - pretty annoying, huh?
Well, that should be a good start on your journey to becoming a Drupal theming expert. Hope you found it helpful!
MORE great resources
- Drupal.org's Theming and Front End Development 'homepage' (free)
- Themery's The definitive guide to Drupal 7 - theming (free)
- Drupalize.me Introduction to Theming Basics for Drupal 7 and Introduction Advanced Theming in Drupal 7 (paid subscription)
- CodeAcademy's PHP course (free)
- Drupal.org Overview of theme files (free)