31st January 2012

Programatically access field data using entity_metadata_wrapper in Drupal

John Ennew
Technical Director

This blog post is about the possible ways to access field data attached to an entity in code.

After loading an entity, such as a node, any fields are attached to the node object as arrays. For example, below is part of a loaded node object:


object(stdClass)#217 (29) {
  ["vid"]=>
  string(1) "1"
  ["uid"]=>
  string(1) "1"
  ["title"]=>
  string(24) "My example node"
  ["body"]=>
  array(1) {
    ["und"]=>
    array(1) {
      [0]=>
      array(5) {
        ["value"]=>
        string(38) "Here is some example text"
        ["summary"]=>
        string(0) ""
        ["format"]=>
        string(13) "filtered_html"
        ["safe_value"]=>
        string(46) "

Here is some example text

" ["safe_summary"]=> string(0) "" } } } }

Here we see the body field as an array attached to the node. To read the value of the data stored in the body text we might access it directly via it's "und" language element:


$body_text = $node->body['und'][0]['value'];

Or, more correctly:


$body_text = $node->body[LANGUAGE_NONE][0]['value'];

However, this approach does not work in all cases as the 'und' key will change if the locale module is enabled. For English language sites this will be 'en'. In multi language sites there may be a number of different keys. We need a way of identifying the language the current user has selected and always read that key.

One approach is the field_get_items() function. This returns just the array of values for the user's configured language.


$body_values = field_get_items('node', $node, 'body');
$body_text = $body_values[0]['value'];

This works fine and gets round the language problem but it doesn't provide very clean code. Imagine if you needed to read more than one field, you would need to run this for each field...


$body_values = field_get_items('node', $node, 'body');
$body_text = $body_values[0]['value'];
$some_taxonomy_values = field_get_items('node', $node, 'field_taxonomy_a');
$some_taxonomy_term = taxonomy_term_load($some_taxonomy_values[0]['tid']);

There is another method provided by the entity api module called entity_metadata_wrapper. This returns an object of type EntityMetadataWrapper. This docs for this make it look complicated but that's because it is very powerful and lets you traverse all entity types in a variety of ways. For our purposes here though we need only know the general pattern for accessing entity fields.

Now, in one line we can get the value of the body text field on a node.


$body_text = entity_metadata_wrapper('node', $node)->body->value();

Another example allows us to load a full taxonomy_term entity from a term reference field on the node in one line. This doesn't just return the term tid but the fully loaded term object.


$some_taxonomy_term = entity_metadata_wrapper('node', $node)->field_taxonomy_a->value();

Indeed this would work for any node referenced by a entityreference field on a node


$other_node = entity_metadata_wrapper('node', $node)->field_some_entityref->value();

By convention, we like to call our wrappers the name of the original variable followed by '_wrapper'. So if there is a node called $node, it's wrapper would be $node_wrapper.


$node = node_load(1);
$node_wrapper = entity_metadata_wrapper('node', $node);

Field cardinality

Field cardinality is the number of possible items that can be connected to a field. The behaviour of entity_metadata_wrapper access to the field differs depending on if this either 1 or many. This is set when you made the field and added it to your entity. So, if you have a node with a taxonomy field called field_taxonomy_a you could specify if this could contain one taxonomy term or more than one (2 to unlimited). If you specified 1 then you would access the data like this:


$node_wrapper = entity_metadata_wrapper('node', $node);
$term = $node_wrapper->field_taxonomy_a->value();

But if you specified more than one, you'll need to specify which term to access in the same way you would access elements of an array, like this:


$node_wrapper = entity_metadata_wrapper('node', $node);
$term = $node_wrapper->field_taxonomy_a[0]->value();

If you do not know the cardinality of a field (perhaps because you are writing polymorphic code that needs to work for both types of situation) then you can use something like this pattern to always get an array.


  $field = 'field_taxonomy';

   // Get the raw value of the field. This will either be
   // an array for multi value fields or a single value
   // for single value fields.
   $values = $node_wrapper->{$field}->raw();
   
   if (!empty($values)) {
    // Make values an array.
    $values = is_array($values) ? $values : array($values);

    // Do something interesting with $values.
  }

Looping through multiple fields via entity_metadata_wrapper

With a multi value field, when you access the field via a wrapper, the object which is returned extends the PHP iterator interface which allows looping over the elements of multiple fields.


  $node_wrapper = entity_metadata_wrapper('node', $node); 
  foreach ($node_wrapper->field_taxonomy_a->value() as $taxonomy) {
    // $taxonomy is a fully loaded taxonomy entity.   
  }  

Note that a field might be setup to have either 1 or multiple values. The type of value returned by the function $wrapper->field_taxonomy_a->value() will change depending on each of these cases. In the case that it can only have 1 value, then that value is returned. If it can have multiple values, an array is returned. For the empty cases, where no term is selected, a multiple field will return an empty array, a single value field will return NULL.

Staying within the wrapper

If you are accessing an entity via a field rather than just a straight value, you might want to wrapper that entity directly. In the example above, removing ->value() will return a wrappered version of the taxonomy term rather than the term itself.


  $node_wrapper = entity_metadata_wrapper('node', $node); 
  foreach ($node_wrapper->field_taxonomy_a as $taxonomy_wrapper) {
    // $taxonomy_wrapper is a taxonomy term wrapped with entity_metadata_wrapper.
    $description = $taxonomy_wrapper->body->value();  
  }  

Modifying an entity through entity_metadata_wrapper

The wrapper object doesn't just make it easy to read data from an entity, it also provides a standard way of modifying an entity. In the simple case, suppose you would like to change the title of a node.


$wrapper = entity_metadata_wrapper('node', $node);
$wrapper->title = 'My new title';
$wrapper->save();
node_save($node);

This doesn't add much to the usual node_save procedure but the process is now the same for all entities, no need for different function names for different entity types. However, it becomes more powerful when editing fields. For example, to change the body text ...


	$wrapper = entity_metadata_wrapper('node', $node);
	$wrapper->body = array(
	  'value' => 'My new body text value',
	  'format' => 'filtered_html',
    );
	$wrapper->save();

You can make several modifications to fields of a node using the same wrapper before finally calling save.

Sanitising your output

When using entity_metadata_wrapper() to retrieve the value of a field, don't forget that if you are going to present that value to the end user, it will need to be run through it's appropriate filtering function. To do that, you can pass array('sanitize' => TRUE) into the ->value() function


$safe_value = entity_metadata_wrapper('node', $node)->field_some_plain_text_field_with_nasty_xss_stuff->value(array('sanitize' => TRUE));

Getting the human name of the bundle

This snippet gets you the human name of the bundle for the entity being wrapped. If the entity is a node, this would be the name of the Content Type (e.g. "News item") $node_wrapper->get('type')->label()

What next?

If you have found this article useful then I recommend reading about how you can extend the object behind Entity Metadata Wrapper to give yourself even more power and make your code much better. Extending EntityDrupalWrapper