Drupal 7 开发内部培训资料, 分头诗人

Drupal7如何使用Entity metadata wrappers (Entity API)

Drupal7如何使用Entity metadata wrappers (Entity API)

Drupal的第三方贡献模块Entiry API提供了一个包装类wrapper,使得在处理实体类型的字段和属性的值的时候更加容易和得心应手。

注:本文翻译自:https://www.drupal.org/documentation/entity-metadata-wrappers 由于英文水平有限,某些句子可能理解不是很到位,如有疑虑请自行查阅源文档。


使用wrapper包装类可以更容易的以一致的编程的方式获取和设置字段的值以及内容。
举例来说,当我们需要从一个node节点中获取某个字段的值,以下这种方法是我们经常在drupal的实体中获取值的方式:

$node->field_number[LANGUAGE_NONE][0]['value'] 

尽管这种方式在大多数情况下可以正常工作,但是这并不理想。比如:用户要求页面需要显示不同的语言或者上面的字段中的'value'并不存在亦或是一个image或file类型的字段呢?
使用Entity模块提供的metadata wrapper我们可以轻松的获取某个字段的值而不用顾虑这些,就像这样:

$node_wrapper->field_number->value(); 

Drupal中所有的Entities实体类型都有某种类型的label。实体类型的label通常是一个对用户友好的字符名称,例如:所有的节点类型都有title的属性、所有用户都有用户名称,如果是一个标准的实体类型我们将很难知道如何正确的处理这些label。Metadata wrapper提供了一个统一的方法去获取不同实体类型的信息。例如:

// Unified way of getting $node->title, $user->name, ...
$wrapper->label();
// Unified way of getting $node->nid, $user->uid, ...
$wrapper->getIdentifier();
// Unified way of getting $node->type, ...
$wrapper->getBundle();

Examples(例子)

利用一些wrapper的包装类可以很轻松的获取和设置值。
包装一个实体对象,你可以使用程序化的函数,如下:

$wrapper = entity_metadata_wrapper('node', $node);

或者从Entity API 7.x-1.6之后,你也可以使用Entity::wrapper()。

$wrapper = $entity->wrapper();

但这仅限于如果$entity 对象使用了Entity API模块提供的Entity类的情况下。
wrapper支持使用链式操作来获取实体的属性,例如:获取一个node节点的作者的mail address信息:

$wrapper->author->mail->value();

更新用户的mail address:

$wrapper->author->mail->set('sepp@example.com');

或者也可以这样写:

$wrapper->author->mail = 'sepp@example.com';

wrappers总是返回一个描述性的数据对象,可以通过entity_get_property_info()来从一个wrapper中获取:

$mail_info = $wrapper->author->mail->info();

获取文本的值并进行对应的清理工作可以这样:

$wrapper->title->value(array('sanitize' => TRUE));

一般node的title是纯文本类型的,但是body字段却可能是带有标签等有害内容的,如何获取body字段并进行相应的过滤处理呢?

$wrapper->body->value->value(array('decode' => TRUE));

这样总会获取清理或者过滤后的数据呈现给用户,但是如果你确实需要获取未加工处理的原始数据,你可以这样:

$wrapper->body->raw();

更多的例子请参考Entity模块中的tests文件。

Working with lists(使用列表)

一个包含wrappers的多值的列表数据,例如多值引用的字段,可以这样进行迭代:

$wrapper = entity_metadata_wrapper('node', $node);
foreach ($wrapper->field_taxonomy_terms->getIterator() as $delta => $term_wrapper) {
  // $term_wrapper may now be accessed as a taxonomy term wrapper.
  $label = $term_wrapper->label->value();
}

给一个多值列表设置值有多种实现方式:
可以将整个值作为一个数组进行设置:

$containing_node = node_load($nid);
$w_containing_node = entity_metadata_wrapper('node', $containing_node);
$nids_to_set = array(42, 23);
$w_containing_node->field_entity_reference_field->set($nids_to_set);

也可以使用标准数组的方括号语法来为字段设置值:

// Setting or adding to a list using square bracket syntax
  $containing_node = node_load($nid);
  $w_containing_node = entity_metadata_wrapper('node', $containing_node);
  
// This appends to what is already there, just like a normal array.
  $w_containing_node->field_entity_reference_field[] = 42;
  $w_containing_node->field_entity_reference_field[] = 23;

最后你也可以从获取字段的数组数据,处理并返回:

// Add to a list using the whole array.
  $containing_node = node_load($nid);
  $w_containing_node = entity_metadata_wrapper('node', $containing_node);
  
$curr_list = $w_containing_node->field_entity_reference_field->value();
  if (!$curr_list)
    $curr_list = array();
  $curr_list[] = $new_nid;
  $w_containing_node->field_entity_reference_field->set($curr_list);
  $w_containing_node->save();

Deleting values(删除值)

wrapper并没有提供->delete()方法来删除一个字段的值,如果想正确的删除一个字段的值可以这样做:

// Using an empty ->set(NULL) removes the value - without NULL you'll get a PHP notice that set($value) requires 1 parameter.
$wrapper->field_data->set(NULL);
// And handles correctly the deltas when using multiple values fields
$wrapper->field_data[$delta]->set(NULL);

Example of using value(), set() and save() (使用value()、set()、和save()方法)

我们先获取一个字段的值,然后使用set()为它设置值并保存:

$node_wrapper = entity_metadata_wrapper('node', $node);
$var = $node_wrapper->field_number->value() + 1;
$node_wrapper->field_number->set($var);
$node_wrapper->save();

如果需要保存一个文件或图片的字段的值,你需要传递键名为'fid'给set()函数,例如:

$containing_node = node_load($nid);
  $w_containing_node = entity_metadata_wrapper('node', $containing_node);
  
// Load the file object in any way
  $file_obj = file_load($fid);
  $w_containing_node->field_attachment_content->file->set( $file_obj );
  // ..or pass an array with the fid
  $w_containing_node->field_attachment_content->set( array('fid' => $fid) );
  $w_containing_node->save();

Example of creating a node:(创建一个节点)

你需要为新的节点设置节点的内容类型和用户ID,然后你就可以用entity_metadata_wrapper()的对象来处理它了:

// Create an Entity.
$node = entity_create('node', array('type' => 'image'));
// Specify the author.
$node->uid = $user->uid;
// Create a Entity Wrapper of that new Entity.
$emw_node = entity_metadata_wrapper('node', $node);
// Set a title and some text field value.
$emw_node->title = 'Test node';
$emw_node->field_text_field = 'Field value text';
// And save it.
$emw_node->save();

Get first value of multifield (multiple-value field)(获取多值字段的第一个值)

只需要在字段名称后面加上索引(数组下标)即可,例如获取第一个的值:

$first_name = $wrapper->field_tags[0]->name->value();

Example using field collections(使用field collections的例子)

// Populate the fields.
    $ewrapper = entity_metadata_wrapper('node', $node);
    $ewrapper->field_lead_contact_name->set($contact_name);
    $ewrapper->field_lead_contact_phone->set($contact_phone);
    $ewrapper->field_lead_contact_email->set($contact_email);  
 
    // Create the collection entity and set it's "host".
    $collection = entity_create('field_collection_item', array('field_name' => 'field_facilities_requested'));
    $collection->setHostEntity('node', $node);  
    
// Now define the collection parameters.
    $cwrapper = entity_metadata_wrapper('field_collection_item', $collection);
    $cwrapper->field_facility->set(intval($offset));
    $cwrapper->save();  
    
// Save.
    $ewrapper->save();

Exceptions(异常处理)

当你使用entity_metadata_wrapper()时将代码放入到try..catch代码块中是一个很值得推荐的做法。这样你可以避免在程序异常的时候不至于中断允许,而且可以在catch代码块中使用watchdog来在后台记录错误日志方便调试,例如:

try {
  $node_wrapper = entity_metadata_wrapper('node', $node);
  $price = $node_wrapper->field_product->field_price->value();
} 
catch (EntityMetadataWrapperException $exc) {
    watchdog(
      'MODULE_NAME',  
      'See '  . __FUNCTION__ . '() <pre>' .  $exc->getTraceAsString() . '</pre>', 
       NULL, WATCHDOG_ERROR
    );
}

Get Start-date and End-date values from Date fields(获取日期字段的开始和结束日期)

如果你有一个日期字段(由Date模块提供),你可以像这样来获取开始/结束的日期:

$wrap_node = entity_metadata_wrapper('node', $node);
$start_date = $wrap_node->field_my_data->value()['value'];
$end_date = $wrap_node->field_my_data->value()['value2'];

或者也可以这样:

$wrap_node = entity_metadata_wrapper('node', $node);
$start_date = $wrap_node->field_my_data->value->value();
$end_date = $wrap_node->field_my_data->value2->value();

Getting information about properties and fields(获取关于属性和字段的信息)

getPropertyInfo()方法可以返回有关属性和字段的变量信息,但是需要给entity_metadata_wrapper()方法传递对应的字段。

// Wrapper for entity type 'node', but not specific bundle.
$wrapper = entity_metadata_wrapper('node');
// Info will contain information only about properties and not about fields.
$info = $this->getPropertyInfo();

现在加上绑定类型:

// Wrapper for entity type 'node' with specific bundle 'page' specified.
$wrapper = entity_metadata_wrapper('node', NULL, array('bundle' => 'page'));
// Info will contain information about both properties and fields.
$info = $this->getPropertyInfo();

Debugging or introspecting wrappers(调试)
获取一个Entity wrapper的所有可用变量的列表,可以使用dpm()来输出(需要安装devel模块并开启):

dpm($wrapper->getPropertyInfo()); 

但这并不会给你任何的属性值,你可以自己写一段代码来进行调试信息的输出:

function _wrapper_debug($w) {
  $values = array();
  foreach ($w->getPropertyInfo() as $key => $val) {
    $values[$key] = $w->$key->value();
  }
  return $values;
}

然后在你的代码中使用它:

dpm(_wrapper_debug($some_wrapped_object));