在没有使用EntitiFieldQuery之前,我一直都是采用db_select的形式,这个我们在前面已经讲过了。我们先来看一个实际的例子,这段代码主要是我写的:
<?php
/**
* @file
* Field validation unique validator.
*
*/
$plugin = array(
'label' => t('Unique values'),
'description' => t('Verifies that all values are unique in current entity or bundle.'),
'handler' => array(
'class' => 'field_validation_unique_validator',
),
);
class field_validation_unique_validator extends field_validation_validator {
/**
* Validate field.
*/
public function validate() {
$flag = TRUE;
$scope = $this->rule->settings['data'];
$count = 0;
foreach ($this->items as $delta1 => $item1) {
if ($this->delta != $delta1) {
if ($this->value == $item1[$this->rule->col]) {
$flag = FALSE;
break;
}
}
}
if ($flag) {
$query = new EntityFieldQuery();
if ($scope == 'global') {
}
elseif ($scope == 'entity') {
$query->entityCondition('entity_type', $this->rule->entity_type);
}
elseif ($scope == 'bundle') {
$query->entityCondition('entity_type', $this->rule->entity_type);
$query->entityCondition('bundle', $this->rule->bundle);
}
list($id, $vid, $bundle) = entity_extract_ids($this->rule->entity_type, $this->entity);
if ($this->rule->entity_type == 'user' && arg(0) =='user' && arg(2) =='edit' && empty($id)) {
$id = arg(1);
}
if ($this->rule->entity_type == 'field_collection_item' && arg(0) == 'field-collection' && arg(3) =='edit' && empty($id)) {
$id = arg(2);
}
if ($this->rule->entity_type == 'profile2' && empty($id)) {
$arg_index = 1;
if (module_exists('profile2_page')) {
$profile_type = profile2_type_load($this->entity->type);
$path = profile2_page_get_base_path($profile_type);
$arg_index = count(explode('/', $path));
}
$uid = arg($arg_index);
if (arg($arg_index + 1) == 'edit' && is_numeric($uid) && $account = user_load($uid)) {
if ($profile = profile2_load_by_user($account, $this->entity->type)) {
$id = $profile->pid;
}
}
}
if (!empty($id)) {
$query->entityCondition('entity_id', $id, '!=');
}
//Always bypass all access checkings.
$query->addMetaData('account', user_load(1));
$query->fieldCondition($this->rule->field_name, $this->rule->col, $this->value);
// Store a copy of our matched entities for our use in tokens later.
$matched_entities = $query->execute();
$count = $query
->count()
->execute();
if ($count) {
$flag = FALSE;
}
}
if (!$flag) {
$token = array(
'[count]' => $count,
);
// Find the first entity that failed this unique condition so we can
// add a token referencing it. First, we have some special handling for
// field collection entities so we can find the entity title of
// whatever the specific field is connected to.
$entity_types = array_keys($matched_entities);
$entity_type = reset($entity_types);
$matched_entity = reset($matched_entities);
$first_match = reset($matched_entity);
$entity_info = entity_get_info($entity_type);
$entity_key_id = $entity_info['entity keys']['id'];
$entitys = entity_load($entity_type, array($first_match->{$entity_key_id}));
$entity = reset($entitys);
if ($entity_type == 'field_collection_item') {
$host_type = $entity->hostEntityType();
$host_entity = $entity->hostEntity();
$label = entity_label($host_type, $host_entity);
$uri = entity_uri($host_type, $host_entity);
}
else {
$label = entity_label($entity_type, $entity);
$uri = entity_uri($entity_type, $entity);
}
$token['[existing-entity-label]'] = $label;
$token['[existing-entity-link]'] = l($label, $uri['path'], $uri['options']);
$this->set_error($token);
}
}
/**
* Provide settings option
*/
function settings_form(&$form, &$form_state) {
$default_settings = $this->get_default_settings($form, $form_state);
//print debug($default_settings);
$form['settings']['data'] = array(
'#title' => t('Scope of unique'),
'#description' => t("Specify the scope of unique values, support: global, entity, bundle."),
'#type' => 'select',
'#options' => array(
'global' => t('Global'),
'entity' => t('Entity'),
'bundle' => t('Bundle'),
),
'#default_value' => isset($default_settings['data']) ? $default_settings['data'] : '',
);
parent::settings_form($form, $form_state);
}
/**
* Provide token help info for error message.
*/
public function token_help() {
$token_help = parent::token_help();
$token_help += array(
'[count]' => t('Count of duplicate'),
'[existing-entity-label]' => t('The label of the first entity that contains matching data.'),
'[existing-entity-link]' => t('A link to the first entity that contains matching data.'),
);
return $token_help;
}
}
这里面涵盖了EntitiFieldQuery的常见用法,首先,我们使用下面的语句,初始化查询语句:
$query = new EntityFieldQuery();
接着,可以为它添加一些条件语句,比如使用entityCondition添加实体本身相关的条件语句:
$query->entityCondition('entity_type', $this->rule->entity_type);
$query->entityCondition('bundle', $this->rule->bundle);
$query->entityCondition('entity_id', $id, '!=');
注意,这里面entity_id,也属于entityCondition的范畴。函数的结构定义是这样的:
public function entityCondition($name, $value, $operator = NULL)
$name,这里的允许值有'entity_type', 'bundle', 'revision_id' 或'entity_id',注意有些实体,比如评论、分类术语,它们不能使用'bundle'。$value就是当前值,$operator是操作符,默认使用“=”,可用的操作符有'=', '<>', '>', '>=', '<', '<=', 'STARTS_WITH', 'CONTAINS', 'IN', 'NOT IN', 'BETWEEN'。注意,后面三个操作符都是作用于数组的。
也可以使用fieldCondition添加字段相关的条件语句:
$query->fieldCondition($this->rule->field_name, $this->rule->col, $this->value);
这是我们这个例子里面用到的。fieldCondition的结构定义是这样的:
public function fieldCondition($field, $column = NULL, $value = NULL, $operator = NULL, $delta_group = NULL, $language_group = NULL)
这里的参数比较多,常用的就是前面四个,后面的我从来没有用过。
在property_validation模块里面的property_validation_unique_validator,还有属性相关的条件语句:
$query->propertyCondition($this->rule->property_name, $this->value) ;
这个成员函数的结构是这样的:
public function propertyCondition($column, $value, $operator = NULL)
$column就是属性名字,$value就是当前值, $operator就是操作符。
加了这么多条件语句以后,使用execute()方法就可以获取查询的结果:
$matched_entities = $query->execute();
当然,我们需要注意,返回的结果的数据结构,通常对于返回的结果,进行这样的处理:
foreach ($matched_entities as $entity_type => $entities) {
foreach ($entities as $entity_id => $entity) {
// 这里进行操作
}
}
如果我们这里已经知道了实体的类型,则可以这样处理:
$result = $query->execute();
$entities = entity_load($my_type, array_keys($result[$my_type]));
由于EntitiFieldQuery里面获取到的实体对象,仅仅是一个壳子,所以,我们还需要使用entity_load来加载它。我们在例子中,为了获取到第一个匹配到的实体,这样处理的:
$entity_types = array_keys($matched_entities);
$entity_type = reset($entity_types);
$matched_entity = reset($matched_entities);
$first_match = reset($matched_entity);
$entity_info = entity_get_info($entity_type);
$entity_key_id = $entity_info['entity keys']['id'];
$entitys = entity_load($entity_type, array($first_match->{$entity_key_id}));
$entity = reset($entitys);
这里使用多次使用reset,用来获取一个数组里面的第一个元素,注意,不能够将array_keys($matched_entities)直接传递给reset,如果这样做的话,会提示有语法问题,但是结果仍然是正确的。
我们这里的例子是这样的:
$count = $query->count()->execute();
另外,这里面需要说明的是,这些成员函数之间,是可以链式调用的。