Drupal → Работа с сущностями с помощью Entity metadata wrappers

17.03.2012

Модуль Entity API позволяет работать с сущностями с помощью своей абстракции под названием Entity metadata wrappers.

Для чего это нужно:

— унифицированный способ доступа к свойствам и полям
— ленивая загрузка дочерних сущностей (например автор материала)
— валидация значений при записи свойств
— простая проверка разрешений

Примеры:

// Создание враппера для ноды по её nid
$node_wrapper = entity_metadata_wrapper('node', $nid);

// Вторым параметром можно передать объект ноды
$node_wrapper = entity_metadata_wrapper('node', $node);

// Получить значение свойства
$nid = $node_wrapper->nid->value(); // или $node_wrapper->getIdentifier();
$title = $node_wrapper->title->value();

// Получить значение свойства, пропущенного через check_plain()
$title = $node_wrapper->title->value(array('sanitize' => TRUE));

// Получить значение поля
$field_value = $node_wrapper->field_name->value();

// Получить первое значение multiple-value поля
$field_value = $node_wrapper->field_name[0]->value();

// Получить значение составного поля
if ($node_wrapper->body->value()) {
  $teaser = $node_wrapper->body->summary->value();
  $body = $node_wrapper->body->value->value();
}

// Получить первое значение составного multiple-value поля
if ($node_wrapper->field_tags->value()) {
  $first_tag_name = $node_wrapper->field_tags[0]->name->value();
}

// Получить значение поля для определённого языка
$field_value = $node_wrapper->language('ru')->field_name->value();

// Обойти значения multiple-value поля
foreach ($node_wrapper->field_tags as $tag_wrapper) {
  $tid = $tag_wrapper->tid->value();
  $tag_name = $tag_wrapper->name->value();
}

// Получить значение свойства у связанной сущности
$author = $node_wrapper->author->name->value();
$author_email = $node_wrapper->author->mail->value();
$author_roles = $node_wrapper->author->roles->optionsList();
$image_url = $node_wrapper->field_image->file->url->value();

// Изменить свойство
$node_wrapper->title = 'New title'
$node_wrapper->save();

// Изменить свойство у связанной сущности
$node_wrapper->author->mail = 'newmail@gmail.com';
$node_wrapper->author->save();

// Изменить поле
$node_wrapper->field_text = 'New value';
$node_wrapper->field_term = 123;
$node_wrapper->save();

// Удалить значения поля
$node_wrapper->field_image->set(NULL);

// Удалить одно значение multi-value поля
$node_wrapper->field_image[0]->set(NULL);

// Проверить наличие поля у бандла (не наличие значения поля,
// а именно существование поля у конкретного бандла)
if (isset($node_wrapper->field_name)) {
  ...
}

// Отличить single-value поле от multi-value
if ($node_wrapper->field_name instanceof EntityListWrapper) {
  // Поле multi-value
}
else {
  // Поле single-value
}

// Получить список доступных свойств
$node_properties = $node_wrapper->getPropertyInfo();
$body_properties = $node_wrapper->body->getPropertyInfo();

// Получить список доступных свойств у определённого типа сущности
$node_properties = entity_get_property_info('node');

Стоит заметить, что эта штука активно используется в Drupal Commerce и Rules.

Хорошая статья на английском — Write better code with EntityMetadataWrapper

Написанное актуально для
Entity API 7.x-1.0-rc1
Похожие записи

Комментарии

выглядит круто. Всегда хотел с Entity API разобраться.

milkovsky
20.03.2012, 19:31

Как можно создать новую node со всеми ее полями?
P.S. не новый node type, а просто новую новость или товар

milkovsky
06.06.2012, 18:29

Можно ли создать WRAPPER для термина таксономии?
например $term_wrapper = entity_metadata_wrapper('taxonomy', $tid);

Николай
06.11.2012, 14:15

Добрый день. Подскажите, как вывести изображение в составном поле?

foreach ($node_wrapper->field_tags as $tag_wrapper) {
$tid = $tag_wrapper->tid->value();
$tag_name = $tag_wrapper->name->value();
}

С этим все понятно, у текстового поля есть value... А как быть с изображением в этой конструкции?
Заранее спасибо

milkovsky
06.11.2012, 14:23

Изображение можно вывести через render array
нужно только получить URI.

http://drupal.stackexchange.com/a/30769

$image = array(
  '#theme' => 'image_formatter',
  '#image_style' => 'thumbnail',
  '#item' => array(
    'uri' => $uri,
    'alt' => '',
    'title' => '',
  ),
);

$form['image'] = $image;
Владимир
11.11.2012, 07:15

обертка для термина таксономии:
$term_wrapper = entity_metadata_wrapper('taxonomy_term', $tid);

Николай
11.11.2012, 09:37

С таксономией все просто. У меня не получается выдернуть поле с изображением в field collections (с текстовым полем тоже просто) :)

@Николай

// одиночное поле
$wrapper->field_collection_name->field_name->value();
// multi-value поле
$wrapper->field_collection_name[0]->field_name->value();
Дмитрий
15.01.2013, 14:59

В очередной раз спасибо! Понятно, подробно и по делу!

Роман
12.02.2013, 01:02

Кстати, по поводу вывода картинки в данном случае.

У меня в таблице клиентов картинка хранится в поле field_client_logo.
И вывести её получилось таким вот образом.

        $cl=entity_metadata_wrapper('node', $node->nid);
        $img=$cl->field_client_logo->value();
        $pr=array(
          '#theme' => 'image_formatter',
          '#image_style' => 'thumbnail',
          '#item' => $img,
        );
        print render($pr);

И если нужно добавить alt или еще какой-нибудь атрибут, то после $img= ...
следует дописать

$img['alt']='Название клиента';
Гость
14.04.2013, 00:20

URI картинки

 $cl=entity_metadata_wrapper('node', $node->nid);
 $img=$cl->field_client_logo->value();
 debug($img); 

Debug:
array (
'fid' => '1885',
'alt' => '',
'title' => '',
'width' => '270',
'height' => '180',
'uid' => '1',
'filename' => 'thumb-video-270x180-689e.jpg',
'uri' => 'public://thumb-video-270x180-689e.jpg',
'filemime' => 'image/jpeg',
'filesize' => '21893',
'status' => '1',
'timestamp' => '1365335160',
)

Отсюда чтобы получить uri картинки нужно .

 $cl=entity_metadata_wrapper('node', $node->nid);
 $img=$cl->field_client_logo->value();
 $uri= $img['uri'] ; 

в статье есть пример правильного получения адреса картинки

А как работать с полями котрые пустые?
Ведь не всегда есть даные в поле. А постоянно получаю если поле пустое:

EntityMetadataWrapperException: 
Unable to get the data property [имя свойства] as the parent data structure is not set

Как то не очень кошерно. Пользователь не ввел данные в поле и сайт падает. В принципе некоторые модули для комерца этим страдают (commerce_product_key).
Может какую то проверку проводить перед обращением к свойству? Я такой не нашел.

Роман
18.12.2013, 12:19

Ну а если через if isset(Ваше_свойство) делать проверку ?

Это не поможет так как такое свойство присутвует

    if (isset($order_wrapper->owner->field_discount_limit->amount)) {
      // field_discount_limit пустое, 
      // если быть точнее то пустой только amount, currency code заполнен, 
      // hook_field_is_empty возвращает TRUE, тоесть при  не залодненой одной котонке поля 
      // поле считается пустым
      dpm($order_wrapper->owner->field_discount_limit->amount->value());

    }

Это не поможет так как такое свойство присутвует

Пока считаю это кривизной проектирования так как и схему поля commerce_price которое не позволяет корректно разрабатывать сайты где есть "дешевые валюты" по отношению к USD типа RUB. В случае рублей пользователю очень легко набрать в корзину по ошибке много товара и он уде не увидит сайт пока администратор и програмисты не удалят его ордер.

На сколько я знаю entity, commerce, field_collection - все эти модули делала толи одна комманда толи один человек (поправьте меня если это не так) и все присутвует та или иная кривизна в проектировании(опять же поправьте если есть другое мнение или факты). Не понимаю почему они не реагируют на те issue которыми кишит d.org по отношению к их модулям.

Работу с entity_metadata_wrapper нужно всегда проверять на EntityMetadataWrapperException.

 try {
    $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item_id);
    // .......
  } catch (EntityMetadataWrapperException $exc) {
    // .......   
  }

Обычно в catch я пишу в watchdog сообщение где вылезла ошибка:

watchdog('feature_oldenbourg_shop', 'See submit quantity in _feature_shop_create_order_preview(). ' . $exc->getTraceAsString(), NULL, WATCHDOG_ERROR);

Если нужно проверить не пусто ли значение пока никакого другого способа не нашел, как загрузка объекта и проверка полей вручную:

$product = $line_item_wrapper->commerce_product->value();
if(!empty($product->myfield)) {
 // .....
}

@Alex, спасибо за развернутый ответ.
Жалко только что он подверждает мое прдеположение о недоделаности entity API. (я о дополнительной проверке поля на пустоту).

@Dima, согласен, что проблема есть. Если есть идеи как это можно улучшить, можешь предложить патч.
Можно ссылочку на issue на drupal.org, меня этот вопрос тоже интересует?

@Alex
Много их, вот что гугл выдает http://goo.gl/nPIIuu.

У меня лично первый раз появилась проблема когда одновременно включил commerce_discount и commerce_product_key.

Что бы патч написать нужно время, которым сейчас к сожалению не обладаю. Но проблему хотелось бы поправить.

@Alex еще раз спасибо, с тем что ты дал уже можно работать.

Очень странное и непредсказеемое поведение, конечно, у объектов этих классов (EntityMetadataWrapper, EntityValueWrapper, EntityStructureWrapper, EntityDrupalWrapper и тд.)

Воспользовался твоим примером:

 
$field_discount_limit = $order_wrapper->owner->field_discount_limit->value(); 

В случаеш пустого поля получаю массив информации о поле, а в случае не пустого пулчаю массив колонком поля (у меня это amount и currency_code).
Это так для информации.

try {
  $value = $wrapper->field_name->value();
} catch () {}

Есть идеи, как получить tnid через metadata wrapper в случае использования i18n?

Привет! есть два кода:

function mymodule_node_presave($node){
  if ($node->type == 'site'){
    $node_wrapper = entity_metadata_wrapper('node', $node);
    $pattern = '(([a-z0-9]*)\.([a-z]{2,7}))';
    $tmp = $node_wrapper->field_site_site->value();
    preg_match($pattern, $tmp, $matches);
    $node_wrapper->field_site_site->set($matches[0]);
    $node_wrapper->save();
  }
}

и

function mymodule_node_presave($node){
  if ($node->type == 'site'){
    $site_name = $node->field_site_site['und']['0']['value'];
    $pattern = '(([a-z0-9]*)\.([a-z]{2,7}))';
    preg_match($pattern, $site_name, $matches);
    dpm($matches);
    $node->field_site_site['und']['0']['value'] = $matches[0];
  }

}

Почему первый вызывает 502-ую ошибку, а второй работает нормально?

потому что после $node_wrapper->save() вызывается hook_node_presave

Артем
09.01.2016, 20:57

Добрый вечер! Можно подробнее о выводе картинок товара!
$commerce_order_wrapper = entity_metadata_wrapper('commerce_order', $commerce_order);
$img = $commerce_order_wrapper->commerce_product->field_dress_img->value();
выводит - Array!

id.voleger
28.01.2016, 18:29

Здравствуйте!
Присутствует ли возможность записывать/читать в поле data объекта user с помощю врапера?

$account = user_load($uid);
$account->data['some_key'];
/* --- --- --- */
$umw = entity_metadata_wrapper('user', $uid);
$umv->/* ? */;

смотрите два последних примера

id.voleger
30.01.2016, 12:50

Похоже что ЕМВ не предоставляет доступ к data объекта $user, либо из за того, что поле сериализуется, либо потому, что возвращаемый объект имеет собственное свойство data.

Игорь
20.04.2016, 21:07

Проверить наличие поля у бандла - есть в статье
А как узнать(увидеть) все поля у бандла?
Это можно как то вывести?

$commerce_order_wrapper = entity_metadata_wrapper('commerce_order', $order_id);
dsm( $commerce_order_wrapper->getPropertyInfo() ); // так всё норм
// но мне нужно дальше, но так не работает:
dsm( $commerce_order_wrapper->commerce_line_items->getPropertyInfo() ); // ??
$commerce_order_wrapper->commerce_line_items[0]->getPropertyInfo()
Игорь
20.04.2016, 21:33

А дальше как?
а то так не работает:

$commerce_order_wrapper->commerce_line_items[0]->commerce_product[0]->getPropertyInfo()

заранее спасибо.

$commerce_order_wrapper->commerce_line_items[0]->commerce_product->getPropertyInfo()
Бакыт
10.05.2016, 20:03

Для того чтобы получить данные свойства picture у юзера
$user_wrapper->value()->picture

Игорь
11.06.2016, 11:02

А как правильно проверить - что это мультипле поле или может иметь только одно значение?

if($field_value = $node_wrapper->field_name)
 
if($field_value = $node_wrapper->field_name[0])
if ($node_wrapper->field_name instanceof EntityListWrapper)
Алексей
11.08.2016, 22:36

Как правильно сохранить поле с несколькими значениями поля.

$node->gallery->set(array(OBJECT_1,OBJCT_2, ...,));

Не прокатывает
Fatal error: Cannot use object of type stdClass as array in /sites/all/modules/entity/modules/callbacks.inc on line 638

Алексей
11.08.2016, 22:42
foreach($photos_files as $file) {
$node->gallery[]->file = $file;
}

...помогло...

Для того чтобы установить новое значение для неограниченного поля
$node_wrapper->field_user_photos[$key]->set( array( 'fid'=> $values['photos'][$key]['fid'] ) );
А вот как удалить одно из значений?
$node_wrapper->field_user_photos[$key]->set(null);
пробовал разные варианты ->set(array), file->set(null)

Если вы используете поле которое ссылается на сущность и оно будет пустое, то вылезет ошибка:
EntityMetadataWrapperException: Unable to get the data property name as the parent data structure is not set.

isset() не поможет используйте getIdentifier()

if($wrapped_entity->entity_reference_field->getIdentifier()) {
  // This code only fires if there is an entity reference or field collection set.
}

Добавить комментарий