Qual é o melhor desempenho: entity_metadata_wrapper ou field_get_items?

10

Para obter valores de entidades, há duas maneiras:

  • Use field_get_itemse obtenha o valor de um campo
  • Use entity_metadata_wrappere obtenha o valor de um campo

Embora entity_metadata_wrapperabstraia as diferenças de idioma, sua API ainda é estranha às vezes, especialmente ao usar o PHP 5.3. Por exemplo, obter o valor de um campo de texto longo geralmente segue esta rota:

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

Felizmente, PHP 5.4 suporta esta sintaxe: print $wrapper->field->value()['safe_value'];.

Mas minha pergunta está mais preocupada com o desempenho. Como eles funcionam? Eles consultam o banco de dados toda vez que solicitam um valor? Será que entity_metadata_wrapperpede tudo de uma vez? (Tornando-se field_get_itemmais adequado para recuperações de valor único.)

Eu não sou corajoso o suficiente para mergulhar fundo na fonte Drupal.

Florian Margaine
fonte
1
field_view_field()é para renderizar um campo. A função para obter o valor de um campo é field_get_items () .
kiamlaluno
E field_get_items()incorre zerar sobrecarga de banco de dados, então eu acho que é um caso bastante aberta e fechada :)
Clive
@Clive, como é que isso field_get_items()gera zero custo de banco de dados? Tem que ter seus dados em algum lugar, certo?
Florian Margaine
Além disso, estou realmente interessado em saber como entity_metadata_wrapperfunciona, em termos de desempenho.
Florian Margaine
2
Você passa um objeto de entidade totalmente carregado para field_get_items()assim a sobrecarga já foi incorrido ... é um pouco de uma rota estrangulada em D7 para ser honesto
Clive

Respostas:

12

A resposta curta: field_get_items () tem mais desempenho que entity_metadata_wrapper ().

Confira o código para estas funções:

Ambos exigem que você repasse a entidade, que já foi carregada do banco de dados . Por exemplo:

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

ou, como você já sugeriu:

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

Ambas as instâncias me incomodam por causa da lógica boba de tentar obter um valor que já está disponível para você, mas elas certamente são úteis em muitos casos.

Você poderia simplesmente fazer print $node->field_my_field_name[LANGUAGE_NONE][0]['value'];isso, mas isso geraria erros de aviso do PHP se o campo não tiver um valor, pois você está tentando acessar matrizes que podem não existir (ou seja [LANGUAGE_NONE][0]['value']). Ultimamente, me vejo fazendo isso frequentemente:

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

o que é muito mais limpo do que fazer:

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

Se você olhar para o código, field_get_items())verá que ele não está fazendo mais nada, garantindo que a matriz do campo tenha dados no idioma atual e, em seguida, retorne-os. Portanto, a sobrecarga de executar uma função tão minúscula é desprezível, mas se você estiver realmente preocupado com o desempenho, pode fazer sua própria verificação se os dados existem e depois imprimi-los.

Edit: Como as field_get_items()execuções field_language()realmente teriam um impacto maior no desempenho do que apenas verificar o idioma, portanto, se você já sabe que a linguagem $ entity-> existe, basta escrever sua própria função de super desempenho:

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
fonte
Ok, além dessas verificações, a entidade é carregada uma vez, não importa quantas vezes eu as use? Mesmo se eu usar referências de entidade?
Florian Margaine
Sim, esse é realmente um recurso muito interessante da API da entidade no D7. Depois de carregar uma entidade, ela é armazenada em cache pela duração dessa solicitação. Portanto, se você fizer o $node = node_load(123);script 1 e fazer isso novamente em outro lugar, não incorrerá na sobrecarga de desempenho de uma carga e construção completa do objeto - o Drupal apenas atribui a essa variável uma cópia da entidade existente. Se você deseja carregar uma nova cópia, precisará passar $reset = TRUEpara a função de carregamento da entidade. Além disso, veja minhas edições sobre um getter de alto desempenho.
00730 Charlie Schliesser
1
if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) {não é necessário, isset($node->field_my_field_name[LANGUAGE_NONE][0]é suficiente.
@chx Eu concordo, mas não seria isset($node->field_my_field_name[LANGUAGE_NONE]), já que o idioma não será definido em um campo vazio? Eu acho que é o delta / [0]que é redundante.
Charlie Schliesser
1
@GilesB, mais consultas ao banco de dados geralmente não são melhores do que junções. O carregamento ansioso é uma técnica de otimização. Mas, mesmo dizendo isso, acho que sua suposição é falsa e o EntityMetadataWrapper provavelmente é mais lento, mas é muito mais agradável de usar. Essa também é uma OP de micro otimização que você não precisa pensar ao trabalhar com o Drupal.
Nicholas Ruunu