Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Loading Only One Field From An Entity or Node in Drupal 7

Parent Feed: 

Time from time, while doing your custom Drupal code, you may want to load only one or several specific fields from a defined set of entities. So actually you have three approaches to this:

1. Query for entity/node set and load whole entities to get desired fields data. Works, but not a performant solution.

2. Make a direct sql query and get desired fields out of your database. Works too, is the fasted in terms of performance solution, but not too flexible and portable.

3. Leverage EntityFieldQuery() and field_attach_load(). This approach is not as fast as the second, but way more faster than loading whole nodes, it is flexible and uses field caching mechanism. If you'll decide to change your database backend later in the future, let's say to MongoDB, you'll be able to switch without changing a line in your code, neat!

But the biggest thing I love about the following approach is that you don't care if you field is single valued or multivalued. You'll get all the values without too much hassle.

So let's get in more details. EntityFieldQuery() allows finding entities based on entity properties (for example, node->changed), field values, and generic entity meta data (bundle, entity type, entity id, and revision ID). More details about what EntityFieldQuery() can do for you find in official docs here: http://api.drupal.org/api/drupal/includes%21entity.inc/class/EntityField...

Now let's imagine a sample task. We'll need a list of image urls of image fields attached to nodes of type "Story". So how do we get this list? Simple, watch my moves:

1. Let's use EntityFieldQuery() to query a list of our article nodes:

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', 'story')
  ->propertyCondition('status', 1)
  ->fieldCondition('field_story_image', 'fid', 'NULL', '!=');
$result = $query->execute();

So we queried for a list of nodes of type "story" and made sure that node has image attached to it. Imagine yourself doing this query via raw sql.

$result should return us a list of entity ids. Node ids should leave in $result['node']:

if (isset($result['node'])) {
  $stories = $result['node'];
}

Yes, we have to check if we have $result['node'] is set, because EntityFieldQuery() returns empty array in case if didn't find any nodes.

2. Now to the most interesting part. Okay, we have an array of node ids, so now we want to load only 'field_story_images' for these nodes. We'll use field_attach_load() to do this:

if (isset($result['node'])) {
  $stories = $result['node'];
 
  // At first we need to get field's id. If you already know field id, you can ommit this step
  // Get all fields attached to a given node type
  $fields = field_info_instances('node', 'story');
 
  // Get id of body field
  $field_id = $fields['field_story_image']['field_id'];
 
  // Attach a field of selected id only to get value for it
  field_attach_load('node', $stories, FIELD_LOAD_CURRENT, array('field_id' => $field_id));
}

I described what we're doing in each step in the code, so everything should be pretty much understandable. In the end, we're getting $stories array, which holds "semi-loaded" nodes with only field we queried attached.

Caveat. field_attach_load() doesn't invoke hook_node_load(), so if some data is being manipulated via hook_node_load() you won't get its representation via described method. This is the only drawback of this method. But in most cases it is safe to use. Just check if it works in your situation.

3. To access your list of values you can do the way you access full node's values, something like this:

  // Get values of our node field
  $output = field_get_items('node', $node, 'field_story_image');

That simple! So for those who could get lost, here's the full code:

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', 'story')
  ->propertyCondition('status', 1)
  ->fieldCondition('field_story_image', 'fid', 'NULL', '!=');
$result = $query->execute();
 
if (isset($result['node'])) {
  $stories = $result['node'];
 
  // At first we need to get field's id. If you already know field id, you can ommit this step
  // Get all fields attached to a given node type
  $fields = field_info_instances('node', 'story');
 
  // Get id of body field
  $field_id = $fields['field_story_image']['field_id'];
 
  // Attach a field of selected id only to get value for it
  field_attach_load('node', $stories, FIELD_LOAD_CURRENT, array('field_id' => $field_id));
 
  // Get values of our node field
  $output = field_get_items('node', $node, 'field_story_image');
}

UPDATE: put this snippet on dropbucket for future reference: http://dropbucket.org/node/54

As you see, the described approach is pretty neat, you get all values of a field in one go and you leverage Drupal cache mechanism. More over, if you want to query for several fields, you need to add just two more lines to our code, let's say, in addition to image fields we want to get a body field, so after:

// Get id of body field
$field_id = $fields['field_story_image']['field_id'];
 
// Attach a field of selected id only to get value for it
field_attach_load('node', $stories, FIELD_LOAD_CURRENT, array('field_id' => $field_id));

you just need to add:

// Get id of body field
$field_id = $fields['body']['field_id'];
 
// Attach a field of selected id only to get value for it
field_attach_load('node', $stories, FIELD_LOAD_CURRENT, array('field_id' => $field_id));

And you'll get a body field attached too! Or in more cleaner way these two fields attachment could look like:

// Get all fields attached to a given node type
$fields = field_info_instances('node', 'story');
 
// Put all field names you want to load
$field_names = array('field_story_image', 'body');
 
foreach ($field_names AS $field_name) {
  // Get id of body field
  $field_id = $fields[$field_name]['field_id'];
 
  // Attach a field of selected id only to get value for it
  field_attach_load('node', $stories, FIELD_LOAD_CURRENT, array('field_id' => $field_id));
}

Wow that was a long read, but if you've survived till the end this means that now you have a powerful tool in your skillset. Go and try it!

Tags 

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web