Quel est le plus performant: entity_metadata_wrapper ou field_get_items?

10

Pour obtenir des valeurs à partir d'entités, il existe deux façons:

  • Utilisez field_get_itemset obtenez la valeur d'un champ
  • Utilisez entity_metadata_wrapperet obtenez la valeur d'un champ

Bien qu'il entity_metadata_wrapperrésume les différences de langage, son API est parfois maladroite, surtout lors de l'utilisation de PHP 5.3. Par exemple, obtenir la valeur d'un long champ de texte suit généralement cette voie:

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

Heureusement, PHP 5.4 prend en charge cette syntaxe: print $wrapper->field->value()['safe_value'];.

Mais ma question concerne plus les performances. Comment fonctionnent-ils tous les deux? Interrogent-ils la base de données chaque fois qu'ils demandent une valeur? Est-ce que entity_metadata_wrapperdemande tout à la fois? (Rendre field_get_itemplus adapté aux extractions à valeur unique.)

Je ne suis pas assez courageux pour plonger profondément dans la source Drupal.

Florian Margaine
la source
1
field_view_field()sert au rendu d'un champ. La fonction pour obtenir la valeur d'un champ est field_get_items () .
kiamlaluno
Et field_get_items()n'encourt aucun frais généraux de base de données, donc je pense que c'est un cas assez ouvert et fermé :)
Clive
@Clive comment se fait-il que la field_get_items()surcharge de la base de données soit nulle? Il doit obtenir ses données quelque part, non?
Florian Margaine
De plus, je suis vraiment intéressé à savoir comment ça entity_metadata_wrapperfonctionne, en termes de performances.
Florian Margaine
2
Vous passez un objet entité entièrement chargé dans field_get_items()donc le surcoût a déjà été encouru ... c'est un peu une route étranglée en D7 pour être honnête
Clive

Réponses:

12

La réponse courte: field_get_items () est plus performant que entity_metadata_wrapper ().

Consultez le code de ces fonctions:

Les deux nécessitent que vous transmettiez l'entité, qui a déjà été chargée à partir de la base de données . Par exemple:

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

ou, comme vous l'avez déjà suggéré:

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

Ces deux cas me dérangent un peu à cause de la logique stupide d'essayer d'obtenir une valeur qui est déjà à votre disposition, mais ils sont certainement utiles dans de nombreux cas.

Vous pouvez le faire, print $node->field_my_field_name[LANGUAGE_NONE][0]['value'];mais cela générerait des erreurs de notification PHP si le champ n'a pas de valeur, car vous essayez d'accéder à des tableaux qui n'existent peut-être pas (par exemple [LANGUAGE_NONE][0]['value']). Je me retrouve à faire cela assez souvent ces derniers temps:

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

ce qui est beaucoup plus propre que de le faire:

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'];
}

Si vous regardez le code, field_get_items())vous verrez qu'il ne fait rien de plus que de vous assurer que le tableau du champ contient des données dans le langage courant, puis de les renvoyer. Ainsi, les frais généraux liés à l'exécution d'une fonction aussi minuscule sont négligeables, mais si vous êtes vraiment préoccupé par les performances, vous pouvez simplement vérifier par vous-même si les données existent, puis les imprimer.

Edit: Étant donné que les field_get_items()exécutions, field_language()il y aurait en fait un impact sur les performances plus important que la simple vérification de la langue, donc, si vous savez déjà que le langage $ entity-> existe, vous pouvez simplement écrire votre propre fonction super performante:

function my_super_performant_field_value_getter($entity, $field_name) {
  return isset($entity->{$field_name}[{$entity->language}]) ? $entity->{$field_name}[{$entity->language}] : FALSE;
}
Charlie Schliesser
la source
Ok, donc à part ces contrôles, l'entité est chargée une fois, peu importe combien de fois je les utilise? Même si j'utilise des références d'entité?
Florian Margaine
Oui, c'est en fait une fonctionnalité plutôt intéressante de l'API d'entité dans D7. Une fois que vous avez chargé une entité, elle est mise en cache pour la durée de cette demande. Donc, si vous le faites $node = node_load(123);dans 1 script et que vous le faites à nouveau ailleurs, vous n'encourez pas la surcharge de performances d'une charge et d'une génération d'objet complètes - Drupal affecte simplement à cette variable une copie de l'entité existante. Si vous souhaitez charger une nouvelle copie, vous devez passer $reset = TRUEà la fonction de chargement d'entité. Voir aussi mes modifications concernant un getter super performant.
Charlie Schliesser
1
if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) {n'est pas nécessaire, isset($node->field_my_field_name[LANGUAGE_NONE][0]suffit.
@chx Je suis d'accord, mais ne le serait-il pas isset($node->field_my_field_name[LANGUAGE_NONE]), car la langue ne sera pas définie sur un champ vide? Je pense que c'est le delta / [0]qui est redondant.
Charlie Schliesser
1
@GilesB, plus de requêtes de base de données ne sont généralement pas meilleures que les jointures. Le chargement avide est une technique d'optimisation. Mais même en disant cela, je pense que votre hypothèse est fausse et EntityMetadataWrapper est probablement plus lent, mais c'est tellement plus agréable à utiliser. C'est aussi une micro optimisation que OP n'a pas à penser lorsque vous travaillez avec Drupal.
Nicholas Ruunu