hook_theme and tpl.php files: registering them with drupal, and passing variables

10.10.2008

There is one way to let drupal know about a theme function or template. There are two ways to send variables to a template, but one of them has a lot of disadvantages.

We'll use two theme functions, with an identical template setup to illustrate these methods:

Creating Theme Functions and Routing Them to a Template

There's only one way, hook_theme()

<?php
/**
* Implementation of hook_theme().
* DRUPAL WONT SEE THIS HOOK UNTIL YOU CLEAR YOUR CACHE
*/
function exampleModule_theme() {
  return array(
   
// as in 'theme('verbose_method',...)
   
'verbose_method' => array(
     
// routes to file exampleModule/spanks-box-thing.tpl.php or [YOUR ACTIVE THEME DIRECTORY]/spanky-box-thing.tpl.php
     
'template' => 'spankys-box-thing',
     
// these variables must be called in the theme function, and will appear in the template as $title, $body, $link
     
'arguments' => array(
       
'title' => null,
       
'body' => null,
       
'link' => null,
      ),
    ),
   
'preprocess_method' => array(
     
'template' => 'spankys-box-thing',
     
'arguments' => array('node' => null),
    ),
  );
}
?>

The Verbose Function Method of sending variables to the tpl.php file

The verbose method is called such because it requires you to first define all the variables in hook_theme, and you have to set all those variables yourself when you call the theme function.

<?php
$output
= theme('verbose_method', $node->title, $node->teaser, l($node->title, 'node/'.$node->nid));
?>

The preprocess method of sending variables to the tpl.php file

Calling this theme function is decidedly less verbose.

<?php

$output = theme('preprocess_method', $node);
return
$output;
?>

However, it requires a little extra code before it magically sends variables with nice names to the tpl.php file. Every theme function automatically looks for a preprocess function. You can preprocess in either the theme, or the module like in this example.

<?php
/**
* Note the pattern [ module_name ] _preprocess_ [ theme_hook ](&$vars)
*/
function exampleModule_preprocess_verbose_method(&$vars) {
// node is only available because we passed it when we called theme function and defined in the arguments array of hook_theme
 
$node = $vars['node'];
 
// $vars['title'] becomes $title in spanky-box.tpl.php
 
$vars['title'] = $node->title;
 
// and so on
 
$vars['body'] = $node->teaser;
// and so on
 
$vars['link'] = l($node->title, 'node/'.$node->nid);
 
// hint: nothing needs to be returned because of it being passed by reference (that's what the little &$ thing means in $vars)
}
?>

Discussion: Why preprocessing is superior

Generally speaking, I only use the preprocess method. Most of the time, I find myself dealing with an object, such as a $node, $user, $comment, $block, or $file. Often, I'm not exactly sure what data I'll need passed to the template when I first write it, so relying on the preprocess functions makes changes easier. Instead of having to change every instance of me having called the function, update the hook_theme function, I only have to alter a line in the preprocess function.

The preprocessing layer gives my brain a break, and doesn't require me to remember all sorts of nonsense like whether the 4th parameter of theme_gizmo is a widget, or a thingamajig. In addition, when dealing with say -- CCK nodes -- I don't have to remember $node->field_something_I_wish_named_better[0]['value'] every time I call the function.

Finally, the layer encourages better code. If I have to turn something into an item_list before it goes into the tpl.php file, I can freely take care of it in the preprocessing layer, instead of doing it before I call the theme function, or in the template file itself. All these arbitrary operations that used to go "where" now have a place. Sure the layer gets ugly at times, but at least the ugliness is contained, and I know where to look for it if I have to change it.

The only time I'll go for the verbose method is when I'm not working $objects, or other complex data types. But I find that to be a rare occurrence.

The template file

spankys-box-thing.tpl.php

<?php
print $title;
?>

<?php
print $body;
?>
<?php
print $link;
?>

Comments

How to pass the whole page

What if I want to theme a page this way? When I chage theme('page', $mypage) to theme('preprocess_method', $mypage) I get nothing?

Thanks

You never directly call

You never directly call "hook_preprocess_tfunction". When theme('page') gets called, it automatically searches for preprocessor functions

function [theme_name]_preprocess_page(&$vars)

or

function [module_name]_preprocess_page(&$vars)

Within these functions you can add or alter data in $vars. For example:

<?php
function blueprint_preprocess_page(&$vars) {
 
// overwrites site name
 
$vars['site_name'] = 'Best Website Ever';
// adds a new variable that will be available as $goober in page.tpl.php
$vars['goober'] ="bob says, yer mom!";
}
?>

If you wish to radically alter the page template based on path you could do it merely by creating a new page.tpl.php.

For example, lets say you had a special path for lightboxes sitename.com/lightbox.
You could add a new page.tpl.php file called page-lightbox.tpl.php.

I thought that's what you

I thought that's what you were doing in your example with preprocess_method.

My goal is to have the module select the template. The page is being saved to a file, not sent to the browser. Ideally the user will select which template to use when the page is saved. My trouble seems to be that I can get the module to use the right template but none of the content is sent to it.

  //Create a full url, the themed page.
  $htmlview  = $base_url .'/'. $dest;
  menu_set_active_item('node/'. $form_state['values']['nid']);
  $test = menu_execute_active_handler();
  $page_html = theme('page', $test );

Then $page_html gets saved to a file.

This on its own will use the default page template. Somehow I need to pass something to _preprocess_page that I can use to tell it which template to use. I thought I was following your example but couldn't get it to work.

Anyway, thanks for looking at this.

I did it like this

  $page_html = theme('htmlsave');

and then

function htmlsave_theme() {
  return array (
    'htmlsave' => array(
      'path' => drupal_get_path('module', 'htmlsave') .'/tpl',
      'template' => variable_get('htmlsave_template', 'htmlsave-fullpage'),
      'arguments' => array(),
    ),
  );
}

And then I reused almost the full theme_preprocess_page to get all my variables in the template.

Thanks for the post. Helped

Thanks for the post. Helped me much

_phptemplate_callback drupal 6 replacement

The reason I'm using such a wierd title is that this is exactly the tutorial I was looking for (plain simple and addresses my need to simply call a tpl from a page).
It's very easy to fall deep into preprocess land and get confused if your not careful.
The next person which is familiar with _phptemplate_callback tpl invocation might find this helpful.
Thanks for this post and others which have been an important part of my themeing education :)
Lior

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.