Theming Field Collections With Preprocess Hooks in Drupal 7

(More readable version)

There are many suggestions for how to go about theming field collections and several confusing problems.

The most important problem is that there appears to be no preprocess hook for field-collection-item--my-field-collection-name.tpl.php (if someone knows otherwise please correct me in the comments, my tests did not work). Next, using field--my-field-collection-name.tpl.php cascades down to all the fields on the field collection, which results in confusing behavior. There is a very long thread on this subject which would take you time to digest.

Most important, as this comment suggests, you can prevent this cascade by adding host entity specificity to the template name: field--my-field-collection-name--my-host-node-name.tpl.php. So for instance if my field collection were named field_fancy_bullets and the node on which it appeared was page my template name would be field--field_fancy_bullets--page.tpl.php.

And so you don't feel bereft, I'll post a little code below for how I'm setting my field markup so it can be simply printed on the field collection. I'm doing some fancy stuff, namely using entity_metadata_wrapper because I like it, and optionally linking content if URL is set in an link field. The code is slightly abstracted because I've been re-using it.

Preprocess code placed in template.php:

 * Implements hook_preprocess_field
 * @param array $vars
 *   An array of variables to pass to the theme template.
function rose_new_preprocess_field(&$vars, $hook) {
  if ($vars['element']['#field_name'] == 'field_fancy_bullets') {
    $vars['rows'] = array(); // Will be keyed by fcol ID and hold rendered fields for printing.
    foreach ($vars['items'] as $index => $entity) {
      try {
        $bundle = key($entity['entity']);
        $fcol_id = key($entity['entity'][$bundle]);
        $entity = $entity['entity'][$bundle][$fcol_id];
        $fcol_w = entity_metadata_wrapper($bundle, $fcol_id);
        foreach (element_children($entity) as $field_name) {
          $row =& $vars['rows'][$fcol_id];
          $row["field_bullet_title_title"] = $fcol_w->field_bullet_title->title->value();
          if (!empty($url = $fcol_w->field_bullet_title->url->value())) { // Optionally link all fields
            $row["field_bullet_title_url"] =
              url($url, $fcol_w->field_bullet_title->value());
            $row["field_bullet_title_linked"] = l($row["field_bullet_title_title"], $row["field_bullet_title_url"]);
          else {
            $row["field_bullet_title_url"] = ''; // if not this then when '', url = '/'
            $row["field_bullet_title_linked"] = $row["field_bullet_title_title"];
          if ($fcol_w->__isset($field_name)) {
            $type = $fcol_w->$field_name->info()['type'];
            if ($type !== 'field_item_link') {
              if ($type == 'field_item_image') {
                $row[$field_name] = render($entity[$field_name]);
              else {
                $row[$field_name] = $fcol_w->$field_name->value();
              if ($row["field_bullet_title_url"]) { // Link all fields if set.
                $row[$field_name] = l($row[$field_name], $row["field_bullet_title_url"], array('html' => TRUE));
      catch (EntityMetadataWrapperException $exc) {
        watchdog('theme_svg_2015', 'EntityMetadataWrapper exception in %function() @trace', array(
          '%function' => __FUNCTION__,
          [email protected]' => $exc->getTraceAsString()
        ), WATCHDOG_ERROR);

And here's my template file, field--my-field-collection-name--my-host-node-name.tpl.php:

 * See my_theme_preprocess_field for preprocessed vars.
//dpm(get_defined_vars()); // Themers, uncomment to see all defined vars.
<?php foreach($rows as $fcol_id => $fields): ?>
  <?php extract($fields); ?>
  <section class="rs-bullets__wrapper">
    <div class="rs-bullets__image"><?php print $field_image; ?></div>
    <div class="rs-bullets__title"><?php print $field_bullet_title_linked; ?></div>
    <div class="rs-bullets__blurb"><?php print $field_bullet_title_linked; ?></div>
<?php endforeach; ?>

About the Author

Hi. My name is Jeremy John. I'm a scifi writer and activist working to build a liberationist Christianity.

Right now, I'm writing a dystopian science fiction novel, and building a website that will connect farms and churches, mosques, and synagogues to buy fresh vegetables directly and distribute them on a sliding scale to those in need.

In 2003, I spent six months in prison for civil disobedience while working to close the School of the Americas, converting to Christianity while I was in the clink.

I'm always looking for dialogue, so kick in below in the comments, connect on Twitter or Facebook, or. . . Read More