4

I am trying to clean up my field-FIELDNAME.tpl.php files to cutdown on the wrapper DIVs that are output.

This is how my code looks:

<?php if(count($items) > 1 ): ?>
  <div class="field-items <?php if($classes) :?><?php print $classes; ?><?php endif ?>"<?php print $attributes; ?><?php print $content_attributes; ?> >
    <?php foreach ($items as $delta => $item) : ?>
      <div class="field-item <?php print $delta % 2 ? 'odd' : 'even'; ?>"<?php print $item_attributes[$delta]; ?>><?php print render($item); ?></div>
    <?php endforeach; ?>
  </div>
<?php else: ?>
  <?php foreach ($items as $delta => $item) : ?>
    <div class="field-items <?php if($classes) :?><?php print $classes; ?><?php endif ?>"<?php print $attributes; ?><?php print $content_attributes; ?> >
      <?php print render($item); ?>
    </div>
  <?php endforeach; ?>
<?php endif ?>

Every field-FIELDNAME.tpl.php file looks the same, the only difference is that the element and the classes applied to the element changes. E.g. In some files, <div class="<Code for printing classes> is replaced with <h3 class="title<Code for printing classes>

As they are all similar, I thought I could put the code into my template.php file as a theme function. Then in my field-FIELDNAME.tpl.php I could call the function and just pass in the bits that change.

Here is how I have adapted the code for my template.php file:

function THEMENAME_field($variables) {
  $variables['element'] = '<div>';
  $variables['class'] = '';
  if(count($items) > 1 ) {
    print '<'.$element.' class="field-items'. $classes . $class. '"'. $attributes .'>';
    foreach ($items as $delta => $item) {
      print '<'.$element.' class="field-item'. $delta % 2 ? 'odd' : 'even' .'"' . $item_attributes[$delta] . '>' . render($item) . '</'.$element.'>';
    }
    print '</'.$element.'>';
  }
  elseif {
    foreach ($items as $delta => $item) {
      print '<'.$element.' class="field-items'. $classes . $class . '"'.$attributes . $content_attributes.'>';
      print render($item);
      print '</'.$element.'>';
    }
  }
}

And this is how I call the function in my field-FIELDNAME.tpl.php

However, it doesn't quite work the way I expect it to. Could someone give me some pointers as to what I am doing wrong?

Thanks!

PS I know about Mothership and Semantic Fields but these are not right for my purpose.


Update: new template.php code following vibaiher suggestion:

function THEMENAME_field($variables) {
$output = '';
$variables['class']= 'standard';
$variables['element'] = 'div';


      foreach ($variables['items'] as $delta => $item) {  
      $items = $item;
      }

if(count($items) > 1 ) {
  $output = '<div class="field-items '. $variables['class'].'"' . $variables['attributes'] .'>';
  foreach ($variables['items'] as $delta => $item) {
    $classes = 'field-item ' . ($delta % 2 ? 'odd' : 'even');
    $output .= '<' . $variables['element']. ' class="'. $classes . '"'. $variables['item_attributes'][$delta]. '>' ;
    $output .=  drupal_render($item) ;
    $output .= '</'.$variables['element'].'>';
    $output .= '</div>';
  }


}

else  {
    foreach ($variables['items'] as $delta => $item) {
      $output .= '<' . $variables['element']. ' class="field-item ' .$variables['class']. '"'. $variables['item_attributes'][$delta]. '>' ;
      $output .= drupal_render($item) ;
      $output .= '</'.$variables['element'].'>';
    }
  }

  return $output;

}

field.tpl.php code:

<?php $element = 'h3' ;?>
<?php $class = 'headline' ;?>
<?php print $output; ?>


Update following Andy's suggestions:

function THEMENAME_field($variables) {
$output = '';
$class = 'standard';
$element = 'div';


      foreach ($variables['items'] as $delta => $item) {  
      $items = $item;
      }

if(count($items) > 1 ) {
  $output = '<div class="field-items '. $class .'"' . $variables['attributes'] .'>';
  foreach ($variables['items'] as $delta => $item) {
    $classes = 'field-item ' . ($delta % 2 ? 'odd' : 'even');
    $output .= '<' . $element. ' class="'. $classes . '"'. $variables['item_attributes'][$delta]. '>' ;
    $output .=  drupal_render($item) ;
    $output .= '</'.$element.'>';
    $output .= '</div>';
  }


}

else  {
    foreach ($variables['items'] as $delta => $item) {
      $output .= '<' . $element. ' class="field-item ' .$class . '"'. $variables['item_attributes'][$delta]. '>' ;
      $output .= drupal_render($item) ;
      $output .= '</'.$element.'>';
    }
  }

  return $output;

}


UPDATE: Further example of what I want to achieve

Let's say I have a field called body text that allows one entry. The output would look like this:

<p class="field-item {$class}" {$itemattributes}> My Field </p>

I then have a field called intro text that has several entries. The output would look like this:

<div class="field-item {$class}" {$itemattributes}>

  <p class="field-item even">my Field 1</p>
  <p class="field-item odd">my Field 2</p>
  <p class="field-item even">my Field 3</p>
  <p class="field-item odd">my Field 4</p>
</div>

I then decided to create a new field called welcome text. I would like to be able to just re-use all the code for the other fields, but swap out the P tag with a H3 and swap the classes. That way, if I ever make any changes to my master field function, these changes will show up any new fields I create.


Update: 11/10/12 - Latest Version of Andy's Code:

/**
 * Implements hook_preprocess_field().
 */
function MYTHEME_preprocess_field(&$variables) {


  $variables['multiple'] = (count($variables['items']) > 1);

  // Add the field-item class.
  if (!in_array('field-item', $variables['classes_array'])) {
    $variables['classes_array'][] = 'field-item';
  }



  // Select the correct tag to use.
  if (($variables['element']['#field_name']['MY_FIELD']) == true) {
    $variables['tag'] = 'h2';
        $variables['classes_array'][] = 'my_title';
  }

else {
   $variables['tag'] = 'b'; 
}

}
  • Why you use print function instead of store the output in a variable? – vibaiher Oct 3 '12 at 13:48
  • @vibaiher Thanks for taking the time to reply! When you say print function, what do you mean. Do you have a link to any examples. Thanks for any help you can offer! – big_smile Oct 3 '12 at 13:50
  • In function THEMENAME_field($variables), why you use php print function? – vibaiher Oct 3 '12 at 13:54
  • Oh! I didn't realize you couldn't use Print in a function. I have tried taking out the print statements, but it still doesn't work. What is the correct way of doing it? Thanks for any help! – big_smile Oct 3 '12 at 13:57
3
+200

Before any theme function or template is run, the preprocess and process hooks are run. This allows additional variables to be set up ready for use in the template/function. If you've created a new theme function called theme_my_thing(), you would use template_preprocess_my_thing() or template_process_my_thing() to set up the variables. For example take a look at template_preprocess_page() to see the page variables being set up. If you want to add variables to an existing theme hook, you can use hook_(pre)process_HOOK. For example, you can see how shortcut.module modifies the page variables at shortcut_preprocess_page().

So in your case, you could create something like this in your template.php:

/**
 * Implements hook_preprocess_field().
 */
function MYTHEME_preprocess_field(&$vars) {
  if (SOME CONDITION) {
    $vars['tag'] = 'h2';
  }
  else {
    $vars['tag'] = 'div';
  }
}

Any variable you create in $vars will be extracted into the template/function. So the example above makes a variable called $tag available in the template/function scope.

Just as you're doing with this question, I try wherever possible to make templates generic and modify the variables before they get there.

I want to mention a couple of points about your example markup (I don't specialise in front-end btw, but think I'm right here!).

  1. I'd prefer consistency over minimal styling, and having a wrapper around all of the field items when there are more than one, but nothing when only one, is a bit inconsistent imho. I'd expect this to make your styling tougher.
  2. Typically a field has an overall wrapper, a title wrapper, and a content wrapper (which wraps all items), each of which has its own attributes array. For fields, there's also an item_attributes array for each individual item. If you don't use these in your template then you might have compatibility problems with modules that expect them (I guess this would typically manifest as styling issues, or JS that doesn't work).

So basically I don't recommend doing this (: But, I think the following would work (untested). Field template (for a specific field):

<?php if ($multiple): ?>
  <div class="<?php print $classes; ?>"<?php print $content_attributes; ?>>
    <?php foreach ($items as $delta => $item): ?>
      <<?php print $tag; ?> class="field-item <?php print $delta % 2 ? 'odd' : 'even'; ?>"><?php print render($item); ?></p>
    <?php endforeach; ?>
  </div>
<?php else: ?>
  <?php foreach ($items as $delta => $item): ?>
    <<?php print $tag; ?> class="<?php print $classes; ?>"<?php print $content_attributes; ?>><?php print render($item); ?></p>
  <?php endforeach; ?>
<?php endif; ?>

In template.php:

/**
 * Implements hook_preprocess_field().
 */
function MYTHEME_preprocess_field(&$vars) {
  $element = $vars['element'];
  // Only act on the field 'some_field_name'.
  if ($element['#field_name'] == 'some_field_name') {

    $vars['multiple'] = (count($vars['items']) > 1);

    // Add the field-item class.
    if (!in_array('field-item', $vars['classes_array'])) {
      $vars['classes_array'][] = 'field-item';
    }

    // Select the correct tag to use.
    if ($some_condition) {
      $vars['tag'] = 'h3';
    }
    else {
      $vars['tag'] = 'p';
    }

    // Add any other classes/attributes.
    // ...
  }
}
  • Thanks for your help! But I don't quite understand. Shouldn't the 'tag' go in the field--fieldNAME.tpl.php file. I don't understand why it goes in template.php. Thanks for any help you can offer! – big_smile Oct 5 '12 at 18:18
  • 1
    @big_smile The idea is to separate the (possibly complex) logic that creates the variables from the (usually simple) logic that creates the markup. So set the variables in the appropriate preprocess function and Drupal's theme layer will make sure the variables are available in the respective template or theme function. (You'll need to clear the theme registry before your preprocess will work.) – Andy Oct 5 '12 at 22:38
  • I have followed your advice (see first post). But how do I now override the individual fields, so they use the same code as the main field, but I can pass in new values for the class and element? I would appreciate any help you can offer. Also, how can I get the foreach function to work (so it only places DIV with odd/even classes around fields that have more than one entry). – big_smile Oct 9 '12 at 9:43
  • 1
    @big_smile I've given an example I'd expect to work. As I've mentioned in the answer, I don't consider this a good template, but I think it will give the markup you've asked for. – Andy Oct 9 '12 at 16:52
  • 1
    @big_smile You can find everything you need in $vars['element']. To test against the field name you can use $vars['element']['#field_name']. (Btw, if you use @andy when you respond, I'll be notified.) – Andy Oct 10 '12 at 15:22
0

All variables used in templates (tpl.php) in function THEMENAME_field(&$variables) are stored in $variables array.

You can create a variable 'output' inside $variables, and add all html.

In your example:

$variables['output'] = '';
if(count($variables['items']) > 1 ) {
   $variables['output'] .= "<$element class=\"field-items $classes $class\" $attributes>";
foreach ($variables['items'] as $delta => $item) {
        $variables['output'] .= "<$element .......

And then in your tpl.php you only need print $output variable

  • Thanks! I have updated my post with the new code, but it doesn't seem to work. Could you give some pointers as to what I am doing wrong thanks! – big_smile Oct 3 '12 at 14:56
  • Sorry, $class, $classes, ... are undefined, use $variables['classes'] etc.. And you forget de dot in $variables['output'] .= blablabla, the dot adds content to existing variable – vibaiher Oct 4 '12 at 7:24
  • I followed your advice (see my first post for the updated code), but now I get the following error messages: Notice: Undefined variable: classes in THEMENAME_field() Notice: Undefined variable: class in THEMENAME_field() Notice: Undefined variable: attributes in THEMENAME_field() Notice: Undefined variable: element in THEMENAME_field() Notice: Undefined variable: element in THEMENAME_field() – big_smile Oct 5 '12 at 18:16
0

Your function

function THEMENAME_field($variables) {

should not print anything but just calculate an output value returning in the end by

  return $output
}

You can use the $variables but adding values is of no use there. See http://api.drupal.org/api/drupal/modules!field!field.module/function/theme_field/7 as an example. For adding or manipulating variables you should use the preprocess function

function MYTHEME_preprocess_field(&$vars) {

to prepare some existing variable or create new ones which can be used by your field.tpl.php file or function THEMENAME_field($variables)

By defining a theme function THEME_field the template file field.tpl.php is never used. Have you cleared the cache and removed the files from the THEME.info file?

  • Thanks for your help. If I use THEMENAME_field, then how can I override the code of this field with separate classes/elements for each new field? – big_smile Oct 9 '12 at 9:45

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.