Need some help with your project? Contact me

How to choose the right hook for theming a field in Drupal 7

In this article we are going to look at theming the output of specific fields in Drupal 7. Although there are a couple of ways of changing the default display of your fields, we will focus on the recommended one - using the theme_field() function of the Drupal API. But I will also show you the other.

Drupal uses theme_field() to display the values of fields in Drupal 7. If you want to change the way these get displayed, you can override this function in your theme’s template.php file or module. An alternative way of doing this is to copy to your theme the field.tpl.php file from the core Field module and make changes in that. You have a few naming options to then target that template file to the field of your choosing:

  • field--body--article.tpl.php
  • field--article.tpl.php
  • field--body.tpl.php
  • field.tpl.php

The first one would affect the Body field of the Article content type, the second one would affect all fields attached to Article nodes, the third one would affect all the Body fields on the site and the fourth would affect all fields on the site. So if you prefer overriding template files you can do this. However, keep in mind that on sites with many fields, this solution is not really recommended due to the performance impact.

Because it takes longer for the server to process template files than php functions, this can result in a slowdown of your website if you are using many fields. So let’s then not be afraid of php functions and see how to override theme_field() and make changes to a certain field.

When overriding a theme function in Drupal (or implementing a hook), you have to replace the first part of the function (theme or hook) with the machine name of your theme or module that does this. This is the starting point. If we have a module called spaceship, the function would look like this:

function spaceship_field($variables) {
  //php code
}

Whatever you put in this function will be reflected in all the field html outputs across your site. It is however best to just copy the default implementation from the Drupal API page and make modifications to it:

function spaceship_field($variables) {
  $output = '';

  // Render the label, if it's not hidden.
  if (!$variables['label_hidden']) {
    $output .= '<div class="field-label"' . $variables['title_attributes'] . '>' . $variables['label'] . ':&nbsp;</div>';
  }
  // Render the items.
  $output .= '<div class="field-items"' . $variables['content_attributes'] . '>';
  foreach ($variables['items'] as $delta => $item) {
    $classes = 'field-item ' . ($delta % 2 ? 'odd' : 'even');
    $output .= '<div class="' . $classes . '"' . $variables['item_attributes'][$delta] . '>' . drupal_render($item) . '</div>';
  }
  $output .= '</div>';
  // Render the top-level DIV.
  $output = '<div class="' . $variables['classes'] . '"' . $variables['attributes'] . '>' . $output . '</div>';
  return $output;
}

If you save the file, clear your cache and reload the Article node, you’ll see no change. This is because although the site now registered this function overriding the default one, you have made no changes to the default. So if you want to change the display of all the fields on your site, go ahead and modify this code. But let’s see how we can better target various fields as you probably won’t want to do this.

One way is to check inside the $variables parameter and condition your changes to the type of field being rendered. You have some options there you can check out. But if across your site you want to make 5 changes to 5 different fields, it will become difficult to manage with this approach. So let’s see another way.

Similar to the template files, we have more options for overriding this function by using a specific naming convention:

  • THEMENAME_field__body__article()
  • THEMENAME_field__article()
  • THEMENAME_field__body()
  • THEMENAME_field()

You can probably guess yourself which one targets what. And these are simple scenarios. Say you have many fields, you are unsure of the exact name of the field or how to incorporate it into this naming structure, I have a trick that will make things easier for you.

With the Devel module installed and enabled, write this simple theme_field() function:

function spaceship_field($variables) {
  dpm($variables);
}

Save the file, clear the caches and load a node. The Devel module will then display information about all the fields attached to that node. All you have to do is find the one you want to override the display for and look for the theme_hook suggestions. So for example, if you load an Article node which by default has a Taxonomy Term field called Tags, you will get these 4 suggestions for it:

  • field__taxonomy_term_reference
  • field__field_tags
  • field__article
  • field__field_tags__article

I’m sure you can by now understand which one would affect which field and in what context. Once you’ve chosen your appropriate hook, you can go ahead and replace the function you used above with the one you actually want. For all the Tags fields on Article node, you’d go like this:

function spaceship_field__field_tags__article($variables) {
  // your php code (see above)
}

And that’s pretty much it. I recommend you copy the code from the API page and make your modifications from there.

Comments

Very cool article - and a great explanation of how to do this at the theme layer. One question I have is do you know if there is an advantage of changing fields in this way versus using something like the Fences module? Is one way more performant? I'm not 100% sure if a choice made with Fences is exportable (via Features), so your way would at least be version controlled & easily deployable. Just wondering. Thanks!

Hey there,

I am afraid I haven't used Fences yet, though I've heard of it. So unfortunately I cannot give you the answer. I do hope someone who has a bit of experience with the module can comment and perhaps help you out.

Thanks!

Danny

However, keep in mind that on sites with many fields, this solution is not really recommended due to the performance impact.

You mentioned that using a field.tpl.php file has performance impacts vs theme_field. Can you elaborate on what those performance impacts are for a field.tpl.php file vs theme_field()?

Where can I read up on the performance of both options?

Hey there,
Unfortunately I cannot point you to impact studies but this is the standard "warning" that comes with Drupal. You can check in the API page of theme_field().

And also keep in mind that when we talk about performance issues, it is for cases where there are many fields that use the tpl files. I doubt you should have a problem. It's just something to keep in mind. :)

Cheers!

@danny: hi, You need to think about how time is spent for the operating system to search the file into the HD, the time that is spent to read it, the time that is spent to send it to the memory, the time that is spent to theme it (php). And HD is significantly slower than the memory.

However all depends on how complicated it is your final HTML. In some cases, the complexity is so high that it is better to use a physical file that a function.

M.

Functions are always faster than template files, that's just the way it is. As Michael explains, there's the overhead of including the file from disk. This happens over and over again as Drupal is using ob and export functions to send the variables to the template file.

So in case you have a lot of fields, you will notice overhead. But then again, having 100+ fields is a major performance drag anyway for Drupal - although we've added significant patches to D7 and D8 lately to overcome that.

As for fences: it stores the template you select for the field directly in the field instance configuration. So in case you use features to export fields and instances, the configuration is in there. You'll probably need to add fences as dependency on the feature though as that won't be recognized by default.

Actually when i need to get a custom output fpr a field, we use to take advantage of hook_field_formatter_info () and so on... it's a little bit more tricky but you can reuse it and i think it's the better way to keep performances ok

I cannot seem to get any of the _field functions to work with modules - but they work just fine and dandy with the theme in template.php.

Add new comment

You must have Javascript enabled to use this form.