6完善的API函数

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

这个模块提供了更多的API函数,比如entity_create()、 entity_save()、 entity_delete()、 entity_view()、 entity_access(),这些函数可以用来方便的创建、保存、删除、查看实体,最后一个entity_access用来控制访问权限的。除了这几个以外,还有entity_id()、 entity_export()、 entity_import()entity_get_property_info()entity_id()是用来获取id的,entity_export负责导出,entity_import负责导入;entity_get_property_info是用来做什么的?它是用来获取实体上面的属性信息的,注意这里的属性,和Drupal核心里面的属性是不一样的概念,在Drupal7核心里面,属性是属性,字段是字段,而在Entity API模块里面,属性、字段都是属性。

Drupal版本:

6.1 hook_entity_property_info

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

Entity API模块定义了一个新的钩子,hook_entity_property_info(),通过这个钩子函数,就可以定义实体包含哪些属性信息了,这个信息里面还包括,属性的数据类型、获取、设置属性的回调函数。注意,很多常用的模块,都用到了这里定义的属性,比如Rules模块。而前面的entity_get_property_info,则是用来获取hook_entity_property_info()里面的定义信息的。这个钩子函数比较抽象,我们可以看一个例子,在sites\all\modules\entity\modules下面可以找到,Drupal核心中各个子系统的钩子实现。下面这个是用户系统的实现:

/**

 * Implements hook_entity_property_info() on top of user module.

 *

 * @see entity_entity_property_info()

 */

function entity_metadata_user_entity_property_info() {

  $info = array();

  // Add meta-data about the user properties.

  $properties = &$info['user']['properties'];

 

  $properties['uid'] = array(

    'label' => t("User ID"),

    'type' => 'integer',

    'description' => t("The unique ID of the user account."),

    'schema field' => 'uid',

  );

  $properties['name'] = array(

    'label' => t("Name"),

    'description' => t("The login name of the user account."),

    'getter callback' => 'entity_metadata_user_get_properties',

    'setter callback' => 'entity_property_verbatim_set',

    'sanitize' => 'filter_xss',

    'required' => TRUE,

    'access callback' => 'entity_metadata_user_properties_access',

    'schema field' => 'name',

  );

下面的这段则是节点系统的实现:

  $properties['created'] = array(

    'label' => t("Date created"),

    'type' => 'date',

    'description' => t("The date the node was posted."),

    'setter callback' => 'entity_property_verbatim_set',

    'setter permission' => 'administer nodes',

    'schema field' => 'created',

  );

  $properties['changed'] = array(

    'label' => t("Date changed"),

    'type' => 'date',

    'schema field' => 'changed',

    'description' => t("The date the node was most recently updated."),

  );

  $properties['author'] = array(

    'label' => t("Author"),

    'type' => 'user',

    'description' => t("The author of the node."),

    'setter callback' => 'entity_property_verbatim_set',

    'setter permission' => 'administer nodes',

    'required' => TRUE,

    'schema field' => 'uid',

  );

除了这些实体系统以外,字段系统也都被包含了进来,不过字段系统的代码比较抽象:

/**

 * Implements hook_entity_property_info() on top of field module.

 *

 * @see entity_field_info_alter()

 * @see entity_entity_property_info()

 */

function entity_metadata_field_entity_property_info() {

  $info = array();

  // Loop over all field instances and add them as property.

  foreach (field_info_fields() as $field_name => $field) {

    $field += array('bundles' => array());

    if ($field_type = field_info_field_types($field['type'])) {

      // Add in our default callback as the first one.

      $field_type += array('property_callbacks' => array());

      array_unshift($field_type['property_callbacks'], 'entity_metadata_field_default_property_callback');

 

      foreach ($field['bundles'] as $entity_type => $bundles) {

        foreach ($bundles as $bundle) {

          $instance = field_info_instance($entity_type, $field_name, $bundle);

 

          if ($instance && empty($instance['deleted'])) {

            foreach ($field_type['property_callbacks'] as $callback) {

              $callback($info, $entity_type, $field, $instance, $field_type);

            }

          }

        }

      }

    }

  }

  return $info;

}

 

/**

 * Callback to add in property info defaults per field instance.

 * @see entity_metadata_field_entity_property_info().

 */

function entity_metadata_field_default_property_callback(&$info, $entity_type, $field, $instance, $field_type) {

  if (!empty($field_type['property_type'])) {

    if ($field['cardinality'] != 1) {

      $field_type['property_type'] = 'list<' . $field_type['property_type'] . '>';

    }

    // Add in instance specific property info, if given and apply defaults.

    $name = $field['field_name'];

    $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];

    $instance += array('property info' => array());

    $property = $instance['property info'] + array(

      'label' => $instance['label'],

      'type' => $field_type['property_type'],

      'description' => t('Field "@name".', array('@name' => $name)),

      'getter callback' => 'entity_metadata_field_property_get',

      'setter callback' => 'entity_metadata_field_property_set',

      'access callback' => 'entity_metadata_field_access_callback',

      'query callback' => 'entity_metadata_field_query',

      'translatable' => !empty($field['translatable']),

      // Specify that this property stems from a field.

      'field' => TRUE,

      'required' => !empty($instance['required']),

    );

    // For field types of the list module add in the options list callback.

    if (strpos($field['type'], 'list') === 0) {

      $property['options list'] = 'entity_metadata_field_options_list';

    }

  }

}

Drupal核心系统,默认是不支持这个钩子的,但是Entity API模块把核心系统里面的所有涉及到的部分,都实现出来了。这里是包括所有的Drupal核心的字段类型的。我们在这里,前期不需要完全明白这些代码的含义,大致看一下,知道有这么回事就可以了,我们只需要记住,这里面,在属性数组信息里面,包含哪些常用的键就可以了。'label''description''getter callback''setter callback''sanitize''required''access callback''schema field'。有兴趣的话,可以沿着我们提示,把sites\all\modules\entity\modules下面,所有inc文件里面的代码都阅读一遍。注意,verbatim的中文意思为完全)照字面的(地),逐字的(地),里面的代码会遇到这个单词。


Drupal版本:

6.2 entity_metadata_wrapper

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

Entity API模块,还提供了一个元数据封装器函数,entity_metadata_wrapper,通过这个函数的封装,我们可以更加方便的访问实体及其属性。这个函数给我们带来了哪些便利呢?我们通过代码示例来了解一下。

 

比如,获取节点作者的电子邮件地址,可以使用下面的代码:

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

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

我们在这里看到,封装过后的这个对象,在它上面,可以采用链式调用,此外它能够直接的建立关联关系。对于上面的功能,如果不使用封装器的话,我们通常采用这样的代码:

$uid = $node->uid;

$user = user_load($uid);

$mail = $user->mail;

这里,我们的代码也并不复杂,看不出来entity_metadata_wrapper的优越性。据文档里面说,$wrapper->author->mail->value();这段代码,可以省去user_load这个操作,直接通过关联关系获取到用户的电子邮件地址,这样可能会有一点小的性能提升。但是这也是有代价的,代价就是调用了entity_metadata_wrapper通过这个例子,我们可以看到这个函数的基本用法。我们在前面让大家阅读代码的原因,就是为这里做准备的,为什么用的是$wrapper->author?而没有用$wrapper->uid,也没有用$wrapper->user?因为在hook_entity_property_info里面定义的是author

 

为了更新用户的电子邮件地址,我们可以使用下面的代码:

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

或者

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

 

换成我们熟悉的代码,则是:

$user->mail =  'test@test.com';

user_save($user);

 

为了获取电子邮件的属性信息,可以使用下面的代码:

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

 

获取过滤了的节点标题:

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

 

获取原始数据:

$wrapper->body->value->raw();

 

上面的这几个例子都比较简单,我们看一个复杂的:

$wrapper->author->profile->field_name->value();

$wrapper->author->profile->field_name->set('新名字');

在这里,从节点,到节点的作者,再到作者的profile,再到profile上面的字段field_name,上面的一句,用来获取field_name字段的值,下面一句用来为该字段设置一个新值。如果我们自己写代码的话,需要加载用户对象,需要加载profile2对象,访问一个字段的值时,需要知道使用哪个语言来访问,代码写起来就比较复杂了。但是,在这里,一行代码就搞定了,非常方便。最后,我们再多看几个示例用法:

$wrapper->language('de')->body->summary->value();

 

$wrapper->author->mail->access('edit') ? TRUE : FALSE;

$wrapper->author->roles->optionsList();

 

$wrapper->field_files[0]->description = 'The first file';

 

$wrapper->save();

$node = $wrapper->value();


Drupal版本:

6.3 简化了实体类型的定义

作者:老葛,北京亚艾元软件有限责任公司,http://www.yaiyuan.com

这个模块还提供了一个Entity对象,如果我们要定义一个新的实体类型时,只需要继承这个对象就可以了,这个对象帮助我们实现了完整的CRUD操作。

Entity API里面,所有的bundle,这里说的是Bundle本身,也都被处理成为了实体,这是一种可以导入、导出的实体。在Drupal核心中,内容类型的添加、编辑,是没有采用实体的形式的。Entity API模块,在这里走的更远,Bundle本身也处理成为了实体。我们看Profile2模块,在profile2_entity_info里面,可以找到这段代码:

  $return['profile2_type'] = array(

    'label' => t('Profile type'),

    'plural label' => t('Profile types'),

    'description' => t('Profiles types of Profile2 user profiles.'),

    'entity class' => 'ProfileType',

    'controller class' => 'EntityAPIControllerExportable',

    'base table' => 'profile_type',

    'fieldable' => FALSE,

    'bundle of' => 'profile2',

    'exportable' => TRUE,

    'entity keys' => array(

      'id' => 'id',

      'name' => 'type',

      'label' => 'label',

    ),

    'access callback' => 'profile2_type_access',

    'module' => 'profile2',

    // Enable the entity API's admin UI.

    'admin ui' => array(

      'path' => 'admin/structure/profiles',

      'file' => 'profile2.admin.inc',

      'controller class' => 'Profile2TypeUIController',

    ),

  );

通过这段代码,把Profile类型也处理成为实体了。对于这种包括Bundle类型的实体,Entity API还提供了一个管理界面,我们只需要对这个默认的管理界面进行扩展,就可以了。这里使用EntityDefaultUIController来管理默认的界面,Profile2TypeUIController则继承了EntityDefaultUIController

如果我们基于Entity API来定义实体类型的话,此时与ViewsRules的集成都比较友好。Entity API模块提供了Views的基本集成,而Rules模块则是Fago的另一个杰作,本身就是基于Entity API模块的。


Drupal版本: