Что является более производительным: entity_metadata_wrapper или field_get_items?

10

Чтобы получить значения от сущностей, есть два способа:

  • Используйте field_get_itemsи получите значение поля
  • Используйте entity_metadata_wrapperи получите значение поля

Хотя entity_metadata_wrapperабстрагируется от языковых различий, его API иногда все еще неуклюж, особенно при использовании PHP 5.3. Например, получение значения длинного текстового поля обычно идет по этому пути:

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

К счастью, PHP 5.4 поддерживает этот синтаксис: print $wrapper->field->value()['safe_value'];.

Но мой вопрос больше касается производительности. Как они оба работают? Они запрашивают базу данных каждый раз, когда запрашивают значение? Запрашивает ли entity_metadata_wrapperвсе сразу? (Создание field_get_itemболее подходящих для поиска одного значения.)

Я не достаточно смел, чтобы глубоко погрузиться в источник Drupal.

Флориан Маргейн
источник
1
field_view_field()для рендеринга поля. Функция для получения значения поля - field_get_items () .
kiamlaluno
И не field_get_items()требует никаких затрат на базу данных, так что я думаю, что это довольно открытый и закрытый случай :)
Клайв
@ Clive, как field_get_items()получается, что у вас нет базы данных? Он должен где-то получить свои данные, верно?
Флориан Маргэйн
Кроме того, мне действительно интересно знать, как entity_metadata_wrapperработает, с точки зрения производительности.
Florian Margaine
2
Вы передаете полностью загруженный объект-сущность, field_get_items()так что накладные расходы уже были понесены ... если честно, это немного удушенный маршрут в D7
Клайв

Ответы:

12

Краткий ответ: field_get_items () более производительный, чем entity_metadata_wrapper ().

Проверьте код для этих функций:

Оба требуют, чтобы вы передали сущность, которая уже была загружена из базы данных . Например:

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

или, как вы уже предложили:

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

Оба эти случая как бы беспокоят меня из-за глупой логики в попытке получить значение, которое уже доступно для вас, но они, безусловно, полезны во многих случаях.

Вы можете просто сделать это, print $node->field_my_field_name[LANGUAGE_NONE][0]['value'];но это приведет к ошибкам уведомления PHP, если поле не имеет значения, так как вы пытаетесь получить доступ к массивам, которые могут не существовать (то есть [LANGUAGE_NONE][0]['value']). Я часто делаю это в последнее время:

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

что намного чище, чем делать:

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

Если вы посмотрите на код, field_get_items())то увидите, что он ничего не делает, а только проверяет, что массив поля содержит данные на текущем языке, а затем возвращает их. Таким образом, издержки на запуск такой крошечной функции незначительны, но если вы действительно обеспокоены производительностью, вы можете просто проверить, существуют ли данные, а затем распечатать их.

Редактировать: Поскольку field_get_items()запуски field_language()на самом деле повлияют на производительность больше, чем просто проверка языка, поэтому, если вы уже знаете, что существует $ entity-> language, вы можете просто написать свою собственную супер-производительную функцию:

function my_super_performant_field_value_getter($entity, $field_name) {
  return isset($entity->{$field_name}[{$entity->language}]) ? $entity->{$field_name}[{$entity->language}] : FALSE;
}
Чарли Шлиссер
источник
Итак, кроме этих проверок, объект загружается один раз, независимо от того, сколько раз я их использую? Даже если я использую ссылки на сущности?
Флориан Маргэйн
Да, это на самом деле довольно крутая функция API-интерфейса для сущностей в D7. Как только вы загружаете объект, он кэшируется на время этого запроса. Таким образом, если вы выполняете $node = node_load(123);сценарий 1 и делаете это снова в другом месте, вы не понесете накладных расходов при полной загрузке и сборке объекта - Drupal просто назначает этой переменной копию существующего объекта. Если вы хотите загрузить новую копию, вам нужно перейти $reset = TRUEк функции загрузки объекта. Кроме того, посмотрите мои правки, касающиеся супер результативного добытчика.
Чарли Шлиссер
1
if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) {не нужно, isset($node->field_my_field_name[LANGUAGE_NONE][0]достаточно.
@chx Я согласен, но не так ли isset($node->field_my_field_name[LANGUAGE_NONE]), поскольку язык не будет установлен в пустом поле? Я думаю, что это дельта / [0]это избыточно.
Чарли Шлиссер
1
@GilesB, больше запросов к базе данных чаще всего не лучше, чем объединения. Стремительная загрузка - это метод оптимизации. Но даже говоря это, я думаю, что ваше предположение неверно, и EntityMetadataWrapper, вероятно, медленнее, но его гораздо приятнее использовать. Это также микрооптимизация OP, о которой не нужно думать при работе с Drupal.
Николай Рууну