5

I usually do exclusively functionality on a site, and as such I'm familiar with php and Drupals APIs, but I'm mostly lost in the theming layers.

I find myself with these pieces of code, that "work", but doesn't look quite right to me.

// ------ Actual USE of #theme -------
$block->content = array(
  '#theme' => 'MODULE_more_information',
  'dataA' => $dataA,
  'dataB' => $dataB,
  'dataC' => $dataC,
);  
return $block;

// ------ hook_theme to attach the #theme to a template -------
function HOOK_theme($existing, $type, $theme, $path) {
  $base = array(
    'render element' => 'element',
    'path' => drupal_get_path('module', 'HOOK') . '/templates',
  );
  $theme = array(
    'MODULE_more_information' => array(
      'template' => 'MODULE-more-information',
    ) + $base,
  );
  return $theme;
}

// ------ Actual template -------
<?php 
extract($element);
?>

<div class="content">
  <h1><?php print $dataA ?></h1>
  <h1><?php print $dataB ?></h1>
  <h1><?php print $dataC ?></h1>
</div>

What gives me the idea that something is amiss is that the author manually runs extract(), which I've never seen a template do before.

Canonical examples of how to do this "right"?

7

To be honest, aside from the use of extract() (which is obviously bad in and of itself), I think the code is perfectly valid, and respectful of Drupal's processes/conventions.

There are two different ways to pass variables to theme functions in Drupal 7, both provided as an argument to a theme definition in hook_theme(). They are variables and render element.

I can't possibly explain the difference between these two better than the hook_theme() docs:

Use 'render element' if you are theming a single element or element tree composed of elements, such as a form array, a page array, or a single checkbox element.

Use 'variables' if your theme implementation is intended to be called directly through theme() and has multiple arguments for the data and style; in this case, the variables not supplied by the calling function will be given default values and passed to the template or theme function.

In your case you're theming a render array, rather than calling theme() directly, so render element is the one to use.

When you use a #theme property on a render array, the entire element (in your case $block['content']) is passed through to the theme function/template file. The name of the variable that you have access to in the template file is the name you have given to render element in the hook_theme() definition.

So if your hook_theme() defines 'render element' => 'my_lovely_var', you'll have access to the render array as $my_lovely_var in the template file.

I suspect that the author of your code was just taking a small shortcut so he/she didn't have to access the $element array directly with code. Without the use of extract() the template file would/should have looked like this:

<div class="content">
  <h1><?php print $element['dataA'] ?></h1>
  <h1><?php print $element['dataB'] ?></h1>
  <h1><?php print $element['dataC'] ?></h1>
</div>

Just for posterity, this is the same code re-written to make use of variables instead:

The same code using variables would look like this:

// ------ Actual USE of #theme -------
$args = array('dataA' => $dataA, 'dataB' => $dataB, 'dataC' => $dataC);
$block->content = array(
  '#markup' => theme('MODULE_more_information', $args)
);
return $block;

// ------ hook_theme to attach the #theme to a template -------
function HOOK_theme($existing, $type, $theme, $path) {
  return array(
    'MODULE_more_information' => array(
      'template' => 'MODULE-more-information',
      'variables' => array('dataA' => '', 'dataB' => '', 'dataC' => ''),
      'path' => drupal_get_path('module', 'HOOK') . '/templates'
    )
  );
}

// ------ Actual template -------
<div class="content">
  <h1><?php print $dataA ?></h1>
  <h1><?php print $dataB ?></h1>
  <h1><?php print $dataC ?></h1>
</div>

You could even use the following, slightly different, method to build up the block content when using variables:

$block->content = array(
  '#theme' => 'MODULE_more_information',
  '#dataA' => $dataA,
  '#dataB' => $dataB,
  '#dataC' => $dataC,
);
return $block;

This second method makes the block more re-usable as the theme function hasn't already been called, and other parts of the system can still alter the individual elements of the block.

  • 1
    Cool, thanks :) When would it be appropriate to use a render element? What is it? – Letharion May 24 '12 at 17:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.