10

To get values from entities, there are two ways:

  • Use field_get_items and get the value of a field
  • Use entity_metadata_wrapper and get the value of a field

Although entity_metadata_wrapper abstracts away the language differences, its API is still awkward sometimes, especially when using PHP 5.3. For example, getting the value of a long text field usually goes this route:

$field = $wrapper->field->value();
print $field['safe_value'];

Fortunately, PHP 5.4 supports this syntax: print $wrapper->field->value()['safe_value'];.

But my question is more concerned about performance. How do they both work? Do they query the database every time they request a value? Does entity_metadata_wrapper requests everything at once? (Making field_get_item more suited to single-value-retrievals.)

I'm not brave enough to dive deep into Drupal source.

  • 1
    field_view_field() is for rendering a field. The function to get the value of a field is field_get_items(). – kiamlaluno Nov 20 '12 at 14:59
  • And field_get_items() incurs zero database overhead so I think that's a pretty open and shut case :) – Clive Nov 20 '12 at 15:03
  • @Clive how come field_get_items() incurs zero database overhead? It has to get its data somewhere, right? – Florian Margaine Nov 20 '12 at 15:25
  • Also, I'm really interested in knowing how entity_metadata_wrapper works, performance-wise. – Florian Margaine Nov 20 '12 at 15:29
  • 2
    You pass a fully loaded entity object into field_get_items() so the overhead has already been incurred...it's a bit of a strangled route in D7 to be honest – Clive Nov 20 '12 at 16:13
12

The short answer: field_get_items() is more performant than entity_metadata_wrapper().

Check out the code for these functions:

Both require that you pass along the entity, which has already been loaded from the database. For example:

$node = node_load(123);
$items = field_get_items('node', $node, 'field_my_field_name');
print $items[0]['value'];

or, as you've already suggested:

$wrapper = entity_metadata_wrapper('node', $node);
$field = $wrapper->field_my_field_name->value();
print $field['safe_value'];

Both of these instances kind of bother me because of the silly logic in trying to get a value that's already available to you, but they are certainly useful in lots of cases.

You could just do print $node->field_my_field_name[LANGUAGE_NONE][0]['value']; but that would throw PHP notice errors if the field doesn't have a value, since you're trying to access arrays that may not exist (i.e. [LANGUAGE_NONE][0]['value']). I find myself doing this quite often lately:

if ($field = field_get_items('node', $node, 'field_my_field_name')) {
  print $field[0]['value'];
}

which is a lot cleaner than doing:

if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) {
  print $node->field_my_field_name[LANGUAGE_NONE][0]['value'];
}

If you look at the code for field_get_items()) you'll see that it's doing nothing more then ensuring that the field's array has data in the curent language and then returns it. So, the overhead of running such a tiny function is negligible, but if you are really concerned with performance you can just do your own check if the data exists and then print it.

Edit: Since field_get_items() runs field_language() there would actually be a bigger performance hit than just checking the language, so, if you already know that $entity->language exists, you could just write your own super-performant function:

function my_super_performant_field_value_getter($entity, $field_name) {
  return isset($entity->{$field_name}[{$entity->language}]) ? $entity->{$field_name}[{$entity->language}] : FALSE;
}
  • Ok, so apart from those checks, the entity is loaded once, no matter how many times I use them? Even if I use entity references? – Florian Margaine Nov 20 '12 at 15:50
  • Yes, this is actually a pretty cool feature of the entity API in D7. Once you load an entity, it is cached for the duration of that request. So, if you do $node = node_load(123); in 1 script and do that again elsewhere, you don't incur the performance overhead of a full object load and build – Drupal just assigns that variable a copy of the existing entity. If you want to load a new copy, you need to pass $reset = TRUE to the entity load function. Also, see my edits regarding a super performant getter. – Charlie Schliesser Nov 20 '12 at 15:55
  • 1
    if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) { is not necessary, isset($node->field_my_field_name[LANGUAGE_NONE][0] is enough. – user49 Aug 10 '13 at 6:59
  • @chx I agree, but wouldn't it be isset($node->field_my_field_name[LANGUAGE_NONE]), since the language will not be set on an empty field? I think it's the delta/[0] that is redundant. – Charlie Schliesser Aug 11 '13 at 23:31
  • 1
    @GilesB, more database queries are most often not better than joins. Eager loading is an optimisation technique. But even saying that, I think your assumption is false and EntityMetadataWrapper is probably slower, but it's so much nicer to use. This is also micro optimisation OP don't have to think about when working with Drupal. – Nicholas Ruunu Nov 13 '15 at 12:37

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.