Field Type Suggestions in Drupal 7

NOTE: I have submitted a feature request to Drupal 7 to have this included in core - if you have some opinion on that please see Field Type Template Suggestions

Today I finally got around to testing out the new field suggestions in Drupal 7. If you're not familiar with the standard suggestions in short you can either use field templates or override theme_field using a naming convention not unlike we do with preprocess functions. What struck me as kind of odd was the lack suggestions for field types—maybe there's a very good reason for this such as performance, I don't know, but I thought it might be interesting to see if I could use them, if I wanted to.

Your first port of call is going to be the new Field module, specifically field.module. In there your going to find two functions that are of interest:

  • template_preprocess_field
  • theme_field

We're going to be messing with both of these in a second, so go and read up and all the docs that come with them.

I Want Clean Code

What drove me to figure this out in the first place was the default output of theme_field sucking badly for things like lists of terms. Everything is in a DIV, even the labels are in DIV tags. Not very good for semantics or accessibility.

I want my lists of terms to be in list markup—a simple unordered list will do me fine. OK, so how the hell do I do this I wondered.

Homework

First thing was to figure out how suggestions work. Looks like the code is in template_preprocess_field where it populates a variable called $theme_hook_suggestions with an array of candidate suggestions. Sweet, I can add to that.

I can see we have classes based on field type so I must be able to use $element['#field_type'] in a similar way to generate the suggestions. Ok, now I'm set, lets do this.

I'm currently working on Genesis 7.x so instead of the usual boring themeName prefix on functions I'm just copy/pasting code from my editor verbatim, so replace "genesis" with your theme name.

The code

I probably want to be rebuilding the theme registry on every page load during testing so I updated with the new function that does this in D7:

<?php
/**
 * Automatically rebuild the theme registry.
 */
drupal_theme_rebuild();
?>

Next we need to add those suggestions by adding to the array. We do this inside our template_preprocess_field. We really want these suggestions to come first, because if they come last we can't override them with the more specific suggestions. Lets use array_unshift to add them to the beginning of the suggestion array:

<?php
function genesis_preprocess_field(&$vars, $hook) {
 
// Add specific suggestions that can override the default implementation.
 
array_unshift($vars['theme_hook_suggestions'], 'field__' . $vars['element']['#field_type']);
}
?>

Note that I'm sticking with the new convention of double underscores, this for performance reasons I believe.

OK, so now we have a whole bunch of new suggestions to play with. Now I want to actually override theme_field and see if this works. I'm gonna add an override for taxonomy term reference fields:

<?php
function genesis_field__taxonomy_term_reference($vars) {
 
$output = '';

 
// Render the label, if it's not hidden.
 
if (!$vars['label_hidden']) {
   
$output .= '<h3>' . $vars['label'] . '</h3>';
  }

 
// Render the items.
 
$output .= '<ul>';
  foreach (
$vars['items'] as $delta => $item) {
   
$output .= '<li>' . drupal_render($item) . '</li>';
  }
 
$output .= '</ul>';

 
// Render the top-level DIV.
 
$output = '<div class="terms clearfix">' . $output . '</div>';

  return
$output;
}
?>

Now that feels better doesn't it? Nice clean output for all my term reference fields! Hooray!

Of course you can override this again. Lets say you want one of your term reference fields to have different output? You can use the default suggestions for that. In this this example my field is called "mytags":

<?php
function genesis_field__field_mytags($vars) {
 
$output = '';

 
// Render the label, if it's not hidden.
 
if (!$vars['label_hidden']) {
   
$output .= '<h2>' . $vars['label'] . '</h2>';
  }

 
// Render the items.
 
$output .= '<ol>';
  foreach (
$vars['items'] as $delta => $item) {
   
$output .= '<li>' . drupal_render($item) . '</li>';
  }
 
$output .= '</ol>';

  return
$output;
}
?>

Here I've changed to an H2 for the label and an ordered list, and got rid of the wrapper DIV since I don't think I really need it.

All this came out of couple of hours of mucking around with Fields in Drupal 7. I'm pretty raw to this so if someone else has a better way of achieving this I'd love to hear it. For now this appears to work, at least for my examples - I have to say that is not tested with any other field type, so I'd love to hear some feedback from your tests!

Ciao, and rock on drupal_themers.

Last updated 26th May, 2010 - 1:43am

Authored by Jeff Burnz on