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

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');

UPDATE: I wrote a more detailed tutorial on how to access node fields values: Working with Drupal Fields: Getting Single Field Values For Display and Manipulaton.

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', $stories, '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!

Comments

Submitted by indytechcook on Wed, 2013-02-20 15:28

Good Article. I added a link back from the EFQ page. http://drupal.org/node/1343708.

Submitted by Tim on Wed, 2013-02-20 16:21

Cool, thanks.

Submitted by Josh Beauregard on Wed, 2013-02-20 17:17

been loving EFQ recently.
Thanks of now just doing another how to query with efq. what really matters is processing the results.

Submitted by Rich on Thu, 2013-02-21 01:19

Nice write up Tim

Submitted by Andy on Thu, 2013-02-21 19:05

Great tutorial. How would you pull one or more fields from a field collection field associated with a node?

Submitted by David Snopek on Sat, 2013-02-23 21:00

Great article and this approach totally works!

I just wanted to throw out an alternative... If you use the "Entity cache" module, the full entity will be cached (including fields) which makes loading the full entity very fast (not including the first time it's ever loaded):

http://drupal.org/project/entitycache

I like this approach because loading the full entity makes your custom code very easy to read and maintain.

Best regards,
David.

Submitted by Murz on Tue, 2013-04-16 11:25

Also you can use module http://drupal.org/project/efq_extra_field that return field value directly in EFQ result.

Submitted by Yusuf on Mon, 2013-07-29 18:00

Man, while I was reading this, I feel like you were talking directly to me :)
Cool!, thanks...

Submitted by Eric on Thu, 2013-08-29 21:13

Hi, i've read your article and learned quite a bit with it, but i have a problem understanding the nature of the $node variable in the field_get_items() : where does it comes from ? what does it contains ?

$output = field_get_items('node', $node, 'field_story_image');

Submitted by Tim on Fri, 2013-08-30 10:47

Sorry, it should be $stories instead of $node. Corrected.

Submitted by Eric on Fri, 2013-08-30 18:06

Hello

I have problems with field_get_items() returning false at all times. Im trying to get the value of a field i created into a custom content type [all created with the interface].
I copied your code into a new module, changed the field_name and content type ('story' and 'field_story_image' in your code) for my field and content type.
After the field_attach_load function i have an array containing the node infos plus the wanted field. I give this array to field_get_items with the name of the wanted field in order to extract it and all i get is a false ... any idea of what could be the problem here ?

Submitted by Hung Nguyen on Sun, 2014-02-23 17:52

I think that this line does not work because of the $stories:
$output = field_get_items('node', $stories, 'field_story_image');

Because the function requires an entity there, so it should be an element of $stories. Ex: $stories[324]. Tested.

Add new comment

You are here