Friday, July 21, 2017 10:34 am EDT

Template suggestions for Drupal 7 - theme_hook_suggestions

Bri's picture

I decided to take some time to figure out how template suggestions work in Drupal 7 being that the release date is getting very close. At the time of this writing Drupal 7 is in RC 4, but they are saying that it will be released officially on January 5th of 2011. I have been working with 7 for a few weeks and finally decided to see if I could get the template suggestions to work. A few things have changed, but they are not that much different from the way they worked in 6. The first difference is that 7 uses a double "-" for its templates by default. For example, by default with no suggestions in both 6 and 7, you could create a template for a node type. If you had a content type with the machine name of "employee" for example, in Drupal 6 you could create a template named "node-employee.tpl.php" and it would recognize it by default when viewing employee nodes. It works almost the same in 7, except now it uses 2 dashes and would be "node--employee.tpl.php" instead. Again, these will work by default, without having to write any custom suggestions in your template.php file.

Here is the problem I came across when trying to upgrade a site I had done in 6. I had a content type for employees and a view that listed all of the employees. When you clicked on an employee, it would show you that person's complete bio. In Drupal 6, I had 2 template files. I had a template that dictated how the full bio would look (viewing a single employee) and I had a template that would dictate how a node teaser would look. In other words, my view was set to display "nodes" instead of "fields." If you are not using suggestions or adding logic in your template to check and see if $page exists, then creating a default template for employees such as node--employee.tpl.php will cause a problem cause this template will be used on both the view page that is set to display nodes, as well as when you view a single employee's page. Template suggestions to the rescue!

If you are familiar with the Drupal 6 template suggestions code, then this will be a breeze for you. If you are not familiar, have no fear, it's all explained here. Drupal 6 had a variable called "template_files" that was a part of a node object, a page object, and a block object. Using preprocess functions we could alter the contents of template_files array and give it more options for templates. The Drupal 6 code for preprocessing a node looked like the following:

function phptemplate_preprocess_node(&$vars)
{
    //default template suggestions for all nodes
    $vars['template_files'] = array();
    $vars['template_files'][] = 'node';

    //individual node being displayed
    if($vars['page'])
    {    
        $vars['template_files'][] = 'node-page';
        $vars['template_files'][] = 'node-'.$vars['node']->type.'-page';
        $vars['template_files'][] = 'node-'.$vars['node']->nid.'-page';
    }
    //multiple nodes being displayed on one page in either teaser
    //or full view
    else
    {
        //template suggestions for nodes in general
        $vars['template_files'][] = 'node-'.$vars['node']->type;
        $vars['template_files'][] = 'node-'.$vars['node']->nid;
        
        //template suggestions for nodes in teaser view
        //more granular control
        if($vars['teaser'])
        {
            $vars['template_files'][] = 'node-'.$vars['node']->type.'-teaser';    
            $vars['template_files'][] = 'node-'.$vars['node']->nid.'-teaser';
        }
    }
}

If you would like to see the rest of the drupal 6 preprocess functions for template suggestions, the older post is here. So the gist of the code here is to modify the array "template_files" and provide it with more options going from general to specific. The idea in drupal 7 is basically the same with a few changes. As I mentioned before, the naming conventions changed a bit in 7. If you were to enable the devel module and kpr the $node variable, you would see that it contained something like this:

  theme_hook_suggestions (Array, 2 elements) 
       0 (String, 10 characters ) node__blog
       1 (String, 8 characters ) node__63

First you will note that "template_files" does not exist anymore, and has been replaced by "theme_hook_suggestions" instead. You will also notice that since i'm on a blog page, its suggesting for this page that I use node--blog.tpl.php or node--63.tpl.php. However, as you can see, the suggestions are using underscores instead of hyphens. This is like the way form ID's were in Drupal 6 even though templates used the same names with hyphens instead. So that's about it, with a little reworks, we can use the same preprocess node function and make it look more like this:

function [themename]_preprocess_node(&$vars, $hook) {
	
  
  //default template suggestions for all nodes
    $vars['theme_hook_suggestions'] = array();
    $vars['theme_hook_suggestions'][] = 'node';

    //individual node being displayed
    if($vars['page'])
    {    
        $vars['theme_hook_suggestions'][] = 'node__page';
        $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->type.'_page';
        $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->nid.'_page';
    }
    //multiple nodes being displayed on one page in either teaser
    //or full view
    else
    {
        //template suggestions for nodes in general
        $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->type;
        $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->nid;
        
        //template suggestions for nodes in teaser view
        //more granular control
        if($vars['teaser'])
        {
            $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->type.'-teaser';    
            $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->nid.'-teaser';
        }
    }
}

This will give you the option to create, for example, two template files for blog posts. One would be named "node--blog.tpl.php" and the other would be "node--blog-page.tpl.php". The one with "-page" would be used when viewing a single blog entry, and the other would be used when viewing a list or a view of node based blog entries. The other preprocess functions can be modified to use this new approach as well.

Comments

Anonymous's picture

Bri, If you're going to

Bri,

If you're going to copy/paste code from my tutorials to use in your own, I'd appreciate a link back to the original code (at the very least).

http://yuriybabenko.com/blog/extending-drupal-templating

Cheers,
Yuriy Babenko

Bri's picture

I think you are mistaken Yuriy

Yuriy,
If it had come from your site I would have. This is an example for Drupal 7. The 6.x code can be found all over the handbook. That is the first time I have seen your site. I am sorry if you think that I got it from you. Though I would be more than welcome to see what you have on your site and share some of it. I would be happy to link to your site for anything that came from it. Thanks for the comment,

Bri

Anonymous's picture

Bri, While a part of this

Bri,

While a part of this code *IS* reposted in an issue on Drupal.org, this specific code isn't; and it's NOT in the handbook. I know where this code came from because I wrote it, along with those comments. You might not have gotten it from my site directly (but rather from another site which ripped it off from me - of which there are numerous), but you definitely didn't write it either.

Yuriy

Anonymous's picture

i get a blank page

when i change (function (themename]_preprocess_node) to (function[themename]_preprocess_page) I get a blank page..

I need to be able to display on one page in either teaser or full view

Eg: page--blog-page.tpl.php

Anonymous's picture

Confusion on underscores and hypens ?!

Dumping the $vars['theme_hook_suggestions']...
array
0 => string 'node' (length=4)
1 => string 'node__boat' (length=10)
2 => string 'node__28' (length=8)
3 => string 'node__boat-teaser' (length=17)
4 => string 'node__28-teaser' (length=15)

Which is at odd with the Drupal 7 usage of '--' instead. I wasn't expecting that. Once I changed the '__' for '--' then I felt better and so did my site.
A nice clear write up on something that is sometimes confusing... I've been using Drupal for five to six years and Drupal 7 is like learning all over again in places but oh how much better it all is.

Nice one Bri!
Emacs the Viking.

Anonymous's picture

Hasty post!

OK, the code as Bri pastes does not work with Drupal 7, and when I read the Drupal 7 guidelines it also says that you shouldn't directly create the array as you lose what has already been written.

The good news is that I now have it working on my Drupal 7 site and I reduced it to this and it does the job,

function wolfrock_theme_preprocess_node(&$vars, $hook)
{
  if($vars['teaser']) {
    $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->type.'_teaser';   
    $vars['theme_hook_suggestions'][] = 'node__'.$vars['node']->nid.'_teaser';
  }
}

Thanks again Bri for the post!
Hope that helps.
:)
Emacs the Viking

Anonymous's picture

Template suggestion for a view page template?

Hi there, thank you for your writings... Is it possible to create a page template for a view, that is not based on nodes, but fields? The template suggestions inside views seem to be for templates that end up inside the page.tpl.php, not to override it. But perhaps I'm not understanding?

Bri's picture

Theme: information

You can override any of the views templates by going to the view and accessing the "Theme: Information" section. This will show you all of the templates that are being used (the name in bold) and a list of suggestions for templates to override. You can also view the code being used in the default template. If you find one you want to use, simply copy the default template to your theme directory and click the "Rescan template files" button in the "theme:information" section of the view. This will activate the new template instead of the default template. If you really want to change the overall page template for the view, using the suggestions code above should return options specific to the page based on the views name. If you want to elaborate more on what your specifically trying to accomplish I can try to help you further. Good luck!

Anonymous's picture

Please let me know how to theme user login form in drupal 7

Please let me know how to theme user login form in drupal 7 beacause in drupal 6 I can do theming with the help of variale $form But in drupal 7 $form is empty.


function hook_theme($existing, $type, $theme, $path) {
return array(
'user_login' => array(
'variables' => array('form' => NULL),
'template' => 'user-login',
),
'user_register_form' => array(
'variables' => array('form' => NULL),
'template' => 'user-register',
),
);
}

Anonymous's picture

Do you know how to theme the

Do you know how to theme the user register form in Drupal 7?
Maybe I am a bit confused.....but I can't find the name of the correspondent tpl to use. "user-register.tpl.php" doesn't work....am I missing some steps?
Many Thanks in advanced.

Anonymous's picture

I am not getting this to work?

I don't seem to get $vars['node']...? Even when viewing the full node view.. I am using the Release possible change?

Anonymous's picture

Here's a different way to do it.

Node tpl's already have type and nid built in with drupal 7. node–[content-type].tpl.php and node–[nid].tpl.php.

I think what you're looking for is [themename]_preprocess_page(&$vars) {

I couldn't get your example to work and it was throwing errors. I am using Drupal 7.0 (final release).

Ended up doing a lot of testing and a few custom functions later, this is what I came up with to emulate that custom page templating in Drupal 7: http://robmalon.com/drupal-7-0-preprocess-page-templates-theme_hook_sugg...

Bri's picture

Both are acceptable

It all comes down to what templates you want to create and override. I have the code posted there on several 7 sites. My preprocess_node is helpful when you want to create one template for a node list, such as a view of nodes, and the other (w/ _page at the end) for when you want to display that node differently when viewed alone. If you are using a preprocess_page, you can still do the same things and create a different page template as well. For me personally, this is not usually what I am wanting to do. As an example, I might have a content type of "Job posting" and by default, i could create a template such as node--job_posting.tpl.php and drupal would recognize it automatically. But if i wanted to create a view of job posting, and have the view display the node (instead of individual fields), it would use the template i created. It would also use that template when viewing a single job posting node. What i really want is to have a template that shows a custom teaser template for the view, and then my own custom full template when viewing a single job posting. That is where my code comes in handy by suggesting two different templates for the two different pages.

Anonymous's picture

If you want the view to

If you want the view to display special formatting there are tpl files specifically for views. By choosing "unformatted" you'd have the most flexibility to work with your theme in CSS. Classes are like this:

views-row views-row-1 views-row-odd views-row-first

Then with each field class that you have in between there if you needed to do specialized styling. Furthermore you can use views-view-unformatted.tpl.php in that scenario rather than a node tpl which is technically more accurate from a standards perspective. Though I digress, I'm not sure exactly what you're doing either.

For the teaser portion, I would opt to use the $teaser flag in the already provided node--[content-type].tpl.php files (by default) to alter the display for that (http://api.drupal.org/api/drupal/modules--node--node.tpl.php/7/source). For the fact that you're only displaying a couple of fields (probably), there's actually a performance gain to be found in not over using so many tpl files. It all depends on the scale of your site though. I work on sites that get 200k pageviews a day so that kind of thing matters to me :).

Bri's picture

Thanks

Thanks for the comment. You are right about the performance gains. I have used views templates in the past, but typically I steered clear of them whenever possible. At the most recent DrupalCon I heard a presentation on performance optimization and they mentioned the same thing. Basically they said that while using the node display option in views is easy, it certainly costs more when compared to using the fields option. In the future I guess I'll be using fields instead.

Bri's picture

Do you have devel installed?

Hi there,
Do you have the devel module installed and turned on? If you add a 'kpr($vars)' to the preprocess function you are working on, you will be able to see the entire data structure. There isn't actually a "node" array defined cause when you are looking at $vars on the preprocess node page, that is the contents of the node. So if you have any fields (former cck) that are custom, you'd find them at $vars['field_fieldname']. I think it will make more sense if you use the kpr($vars). Let me know if you have more trouble.

Anonymous's picture

Thanks!!

Thank you, I just figured out what I was doing wrong... I was running [theme]_preprocess_html and not [theme]_preprocess_page, so it was indeed leaving out the Node.

But thank you on the 'kpr($vars)' tip didn't know about that.