EMW 的使用:以 Computed Field 为例

(配图由 jungle 合成,图中的人像是 Entity API 模块的作者 fago)

(原始图片)


你是否遇到过:

  • 一个字段的值依赖于其他字段。例如:总金额 = 数量 × 单价。
  • 一个字段的值依赖于其他字段,且其他字段来源于其他实体(如:内容类型、用户、分类词汇)。

以上情形,用 Drupal 社区提倡的 Drupal 方式做事,第一反应是有没有现成模块可用?(Is there a module for that?)答案是有,它就是 Computed Field 模块。

Computed Field is a very powerful field module that lets you add a custom "computed fields" to your content types. These computed fields are populated with values that you define via PHP code. You may draw on anything available to Drupal, including other fields, the current user, database tables, you name it. (Feeling the power yet? :) ) You can also choose whether to store your computed field values in the database with other content fields, or have them "calculated" on the fly during node views. (Although you should note that Views use requires database stored values.) This field is literally the Swiss Army knife of fields. So start cooking up your PHP based values!

Computed Field 模块提供了一个计算类型的字段,该字段的值通过自定义PHP代码计算得到。

在官网提供的一篇关于 [Computed Field(https://www.drupal.org/project/computed_field) 相关的文档中,可以看到以下代码:

$status = $entity->field_marital_status[LANGUAGE_NONE][0]['value'];
$status_display = field_view_value($entity_type, $entity, 'field_marital_status', $status, array());

$entity_field[0]['value'] = 'You are' . render($status_display);

看第一行代码,为了取得 field_marital_status 的值,1. 先取得 LANGUAGE_NONE 对应的数组;2. 再访问第0位置的值,该值仍然是数组;3. 再取得key为'value'。有些麻烦吧,如果这个字段是实体引用类型的字段怎么办,岂不更麻烦?能不能不要这么麻烦!可以,有Entity Metadata Wrapper(EMW)!换成EMW,上面代码是怎样的呢?

$wrapper = entity_metadata_wrapper($entity_type,$entity);
$status = $wrapper->field_marital_status->value();
$status_display = field_view_value($entity_type, $entity, 'field_marital_status', $status, array());

$entity_field[0]['value'] = 'You are' . render($status_display);

看第二行,是不是没那么麻烦了?这一切都得益于 Entity API 模块提供的 EMW。

The contributed Entity API module provides wrapper classes that make dealing with the values of an entity's properties and fields easier.

如果字段是复杂的实体引用类型呢?

以上是为了引入本文的主题。下面将演示三种情形下的计算:

  1. 涉及简单(整型)字段的计算

- 涉及实体引用字段的计算,引用实体个数为1 - 涉及实体引用字段的计算,引用实体个数不限

首先:定义三个内容类型,对应的设置如下:

内容类型 字段
名称 机器名 名称 机器名 类型 数量 特殊设置 说明
Foo foo A field_a 整型 1    
Bar bar B field_b 整型 1    
Baz baz X field_x 整型 1    
Y field_y 整型 1    
Z field_z 实体引用 1 引用到 Foo,数据类型:整型  
ZZ field_zz 实体引用 不限 (n) 引用到 Bar,数据类型:整型  
R field_r 计算 1   R = X + Y
S field_s 计算 1   S = X + Y + Z,Z <=> A
T field_t 计算 1   T = X + Y + ZZ,ZZ <=> B1+ B2 + … + Bn

R字段对应于情形1,两个整数字段的相加;S字段对应于情形2,两个整数字段,以及和通过实体引用,引用的一个整数字段,三个字段的相加;T字段对应于情形3,两个整数字段,以及和通过实体引用,引用的多个整数字段,多个字段的相加。

内容类型 Foo 的字段列表界面:

内容类型 Bar 的字段列表界面:

内容类型 Baz 的字段列表界面:

计算字段的计算和显示是分开的,要分别提供 PHP 代码。

在R字段的字段设置界面的 Computed Code (PHP) 文本框(提供计算代码的地方)的下面,可以发现这样一句话

Alternately, this code can be supplied by your own custom function named: computed_field_field_r_compute(&$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items) 意思是说,实现一个函数名为 computed_field_field_r_compute 的自定义函数,放到自定义的模块里(没有直接说放到自定义模块里,在 Drupal 里提供自定义函数不就是让你写个模块吗,写模块?不要担心,这个模块就几个函数),就可以不用在这里填写 PHP 代码了,计算的逻辑交给 computed_field_field_r_compute 就可以了。

在Baz的R字段的字段设置界面的 Display Code (PHP) 文本框的下面的最后,可以发现下面这句话

Alternately, this code can be supplied by your own custom function named: computed_field_field_r_display($field, $entity_field_item, $entity_lang, $langcode, $entity). Return the value to be displayed. Original value is in $entity_field_item['value']. 同理,这是告诉我们实现一个怎么样的自定义函数来显示字段。

现在我们自定义一个 Demo 模块,这个模块包括一个 demo 文件夹和两个文件:demo.info 和 demo.module,以下是源代码。可以看到使用了 EMW,代码是多么优雅!

demo.info

name = Demo
description = Computed Field & EMW
core = 7.x
package = Jungle

dependencies[] = computed_field
dependencies[] = entityreference
dependencies[] = ctools

demo 模块依赖 computed_filed 和 entityreference,他们分别提供了计算(Computed)字段和实体引用(Entity Reference)字段。entityreference 模块依赖 ctools 所以这里把 ctools 也列为模块的依赖了。

demo.module

function computed_field_field_r_display($field, $entity_field_item, $entity_lang, $langcode, $entity){
  return $entity_field_item['value'];
}
function computed_field_field_s_display($field, $entity_field_item, $entity_lang, $langcode, $entity){
  return $entity_field_item['value'];
}
function computed_field_field_t_display($field, $entity_field_item, $entity_lang, $langcode, $entity){
  return $entity_field_item['value'];
}

以上是显示 R、S、T字段值的代码,可以看到显示的代码是相同的,为什么呢。因为在本例中,显示的值就是就是计算部分返回的值,不需要额外的操作。

R 字段计算部分逻辑的实现:

function computed_field_field_r_compute(&$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items){
  $wrapper = entity_metadata_wrapper($entity_type,$entity);
  $x = $wrapper->field_x->value();
  $y = $wrapper->field_y->value();
  $entity_field[0]['value'] = $x + $y;
}

很简单吧?

S 字段计算部分逻辑的实现

function computed_field_field_s_compute(&$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items){
  $wrapper = entity_metadata_wrapper($entity_type,$entity);
  $x = $wrapper->field_x->value();
  $y = $wrapper->field_y->value();
  $z = 0;
  $wrapper_foo = $wrapper->field_z;
  $foo_id =  $wrapper_foo->raw();
  if (!empty($foo_id)) {
    $z = $wrapper_foo->field_a->value();
  }
  $entity_field[0]['value'] = $x + $y + $z;
}

这里稍微复杂了一点,$wrapper_foo 保存了 字段 Z 的引用。然后再去判断字段 Z 的值是否填写(是否为空),不为空,直接通过 $wrapper_foo 获取 Foo 内容类型里的字段 A 的值。

T 字段计算部分逻辑的实现

function computed_field_field_t_compute(&$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items){
  $wrapper = entity_metadata_wrapper($entity_type,$entity);
  $x = $wrapper->field_x->value();
  $y = $wrapper->field_y->value();
  $zz = 0;
  $wrapper_bars = $wrapper->field_zz;
  $bar_ids =  $wrapper_bars->raw();
  if (!empty($bar_ids)) {
    for ($i=0; $i < count($bar_ids); $i++) { 
      $zz += $wrapper_bars[$i]->field_b->value();
    }
  }
  $entity_field[0]['value'] = $x + $y + $zz;
}

相比 S 字段,要略为复杂一点。$wrapper_bars 保存字段 ZZ 的引用。然后通过这个引用的 raw 方法获取引用的所有 Bar 的实体 id,在这里是 nid,然后判断是否为空。不为空,再通过保存的引用 $wrapper_bars 来获取每个 Bar 中字段 B 的值。

演示

Foo 和 Bar 相关的值通过 Views 列出来,省略其编辑过程

以上是 Baz1 的编辑界面

总结

本文演示了 Computed Field 的使用,本意是让大家接触 Entity,Entity 在 Drupal 中很重要。个人认为它和 Views 同等重要。在Drupal 7 中,内容类型,用户,分类词汇这些都是实体,使用 EMW 操作实体是很方便的。了解实体,对于理解 Drupal 也很重要。如果理解了实体,也许能明白什么是 node 等等。

EMW 只是 Entity 的冰山一角,后续还会介绍较为详细介绍 Entity 相关的内容。

EMW 相关文章:

本文使用的模块:

完整代码:Computed Field Demo

标签

添加新评论

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。