How to render image fields with Drupal 7

April 12, 2011

Suppose you have a Drupal 7 site, with a node containing an image field called field_image, and you want to pull the URL of the image into your page template. (As opposed to a node template, where it’s already rendered.) For bonus points, you want the image to be rendered through a “style” (aka Imagecache preset).

In Drupal 6, this involved theme(‘imagecache’) and $node->field_image[0][‘filepath’]. In Drupal 7 it’s a little different, because the Files API has been abstracted from the local file system (to handle other types of storage) and Imagecache is now (mostly) in the core Image module.

First, I’m separating this into the preprocessor logic where we check if the image field exists and get the right code, and the template output where we use the finished value. The page preprocessor would probably be in your theme’s template.php and look like function THEME_preprocess_page(&$vars), and your page template would look like page.tpl.php.

Fortunately, the node object is already in the page preprocessor’s variables, as $vars[‘node’]. Let’s break this down a little with the available API functions:

// filename relative to files directory
// e.g. 'masthead.jpg'
$filename = $vars['node']->field_image['und'][0]['filename'];

// relative path to raw image in 'scheme' format
// e.g. 'public://masthead.jpg'
$image_uri = file_build_uri($filename);

// relative path to 'styled' image (using an arbitrary 'banner' style) in 'scheme' format
// e.g. 'public://styles/banner/public/masthead.jpg'
image_style_path('banner', $filename);

// raw URL with an image style
// e.g. 'http://mysite.com/sites/default/files/styles/banner/public/masthead.jpg'
// [passing a raw path here will return a very ungraceful fatal error, see http://api.drupal.org/api/drupal/modules--image--image.module/function/image_style_url/7#comment-12079]
$vars['masthead_raw'] = image_style_url('banner', $image_uri);

// html for a styled image
// e.g. '<img typeof="foaf:Image" src="http://mysite.com/sites/default/files/styles/banner/public/masthead.jpg" alt="" />'
$vars['masthead'] = theme('image_style', array('style_name' => 'banner', 'path' => $image_uri));

So to do something useful with this:

function THEME_preprocess_page(&$vars) {
  // pull the masthead image into the page template
  if (isset($vars['node']->field_image) && !empty($vars['node']->field_image['und'][0]['filename'])) {

    $filename = $vars['node']->field_image['und'][0]['filename'];

    $image_uri = file_build_uri($filename);

    // url
    $vars['masthead_raw'] = image_style_url('banner', $image_uri);

    // html
    $vars['masthead'] = theme('image_style', array('style_name' => 'banner', 'path' => $image_uri));
  }
}

Now in your page template, you have $masthead (HTML) and $masthead_raw (URL) [with the $vars variables now being independently named] so you can do something like this in a PHP block:

<?php if ($masthead): ?>
  <div id="masthead"><?php echo $masthead; ?></div>
<?php endif; ?>

A quick-and-dirty alternative would be, directly in page.tpl.php:

if (!empty($node->field_image[‘und’][0][‘filename’]) {
 echo theme(‘image_style’, array(‘style_name’ => ‘banner’, ‘path’ => file_build_uri($node->field_image[‘und’][0][‘filename’])));
}
(note $vars[‘node’] is now $node)

The shorthand version will work, but separating the logic from the output (with a preprocessor and template) is the “best practices” approach.