Four Kitchens
Insights

A better way to theme field collections

3 Min. ReadDesign and UX

Field collections are at the same time one of my most favorite and least favorite aspects of working with Drupal 7. Since they are entities they can be extremely powerful and flexible site building tools, and I see lots of unrealized potential in that, on the other hand theming can be tricky and, for lack of better word, generally feels “icky.” There is little documentation online about best practices with almost all links pointing back to this thread on how to theme field collections. The proposed solutions in this thread are a mixed bag — mostly bad — but some that may work, but they certianly don’t follow any best practices in drupal theming. I’ll admit I have shipped field collection theming that, while working, did make me feel “dirty.” Below was a clean solution that — while simple — is maintainable and, hopefully easy to follow.

I created a quick function that pulled out the themed items from the field collection and organized them into an easy to theme array. The theming then felt a lot more natural, like working with a views rows template.

/**
 * Creates a simple text rows array from a field collections, to be used in a
 * field_preprocess function.
 *
 * @param $vars
 *   An array of variables to pass to the theme template.
 *
 * @param $field_name
 *   The name of the field being altered.
 *
 * @param $field_array
 *   Array of fields to be turned into rows in the field collection.
 */

function rows_from_field_collection(&$vars, $field_name, $field_array) {
  $vars['rows'] = array();
  foreach($vars['element']['#items'] as $key => $item) {
    $entity_id = $item['value'];
    $entity = field_collection_item_load($entity_id);
    $wrapper = entity_metadata_wrapper('field_collection_item', $entity);
    $row = array();
    foreach($field_array as $field){
      $row[$field] = $wrapper->$field->value();
    }
    $vars['rows'][] = $row;
  }
 }
}

In this function we pull in the “variables” array from the field template, the field name and an array of fields that we want to extract, and then load them into our own rows array which gets attached to the variables array for use in our template. It uses the entity_metadata_wrapper that is like a swiss army knife of sanity and clean code for Drupal 7. If you aren’t familar with it, start with this documentation page about entity metadata wrappers, and follow up with some quick googling. It’ll make you and your code happy.

Below is the function being called and our custom field template being established. One thing that keeps our code simple is that all fields in the field collection are required eliminating the need to check for the variables existence in the template.

function THEME_preprocess_field(&$vars, $hook){
  if ($vars['element']['#field_name'] == 'field_achievements') {
    $vars['theme_hook_suggestions'][] = 'field__achievements_collected';

    $field_array = array('field_award', 'field_award_presenter','field_year');
    rows_from_field_collection($vars, 'field_achievements', $field_array);
  }
}

Finally below is the field template.

/**
 *  Field formatter for the field_achievements field collection.
 */
< php foreach($rows as $row): ?>
< php print $row[‘field_award’]; ?>
< php print $row[‘field_award_presenter’]; ?>
< php print $row[‘field_year’]; ?>
< php endforeach; ?>




We had a number of fields that were just sets of text data that needed to be output in definition lists. The function could be easily modifed to render the fields instead of just the values by using entity_view(). Next steps for this approach would be to refactor to use entity info to get the fields so that it can be generic and would no longer require the need to pass in the fields that you want.

There is hope for theming within the module itself. There is a promising issue in the queue that looks like it makes the native theming more sane. Since it’s such a big change it doesn’t look likely that it’ll make it into the 1.0 branch, so we may need to wait for the next version before it drops. Perhaps there is space for an add-on contrib module that cleans up the theming.