当前位置: 首页>>技术问答>>正文


在EntityFieldQuery中使用OR

webfans 技术问答 , , 去评论

问题描述

我从来没有必要在今天之前执行此操作,但似乎您不能使用EntityFieldQuery进行OR查询,因为db_or用于选择查询。

一个示例将获得具有日期字段的所有实体,其中值为null或在今天之后。

我错过了什么或某些技巧或者这根本不受支持?

最佳解决思路

我见过solution of this problem。我们的想法是在查询中使用addTag()并实现hook_query_TAG_alter(),其中您有旧的SelectQuery对象。

次佳解决思路

您可以子类化EntityFieldQuery并覆盖某些方法。

添加到类EntityFieldQuery的对象的条件(例如属性条件)将添加到数组中。

  public function propertyCondition($column, $value, $operator = NULL) {
    // The '!=' operator is deprecated in favour of the '<>' operator since the
    // latter is ANSI SQL compatible.
    if ($operator == '!=') {
      $operator = '<>';
    }
    $this->propertyConditions[] = array(
      'column' => $column, 
      'value' => $value, 
      'operator' => $operator,
    );
    return $this;
  }

构建查询时,该数组将在类似于下一个循环的循环中使用(代码存在于EntityFieldQuery::propertyQuery()中):

foreach ($this->propertyConditions as $property_condition) {
  $this->addCondition($select_query, "$base_table." . $property_condition['column'], $property_condition);
}

$select_query包含从db_select()调用返回的值。

第三种解决思路

无需将查询拆分为2并合并或类似的任何内容。只需要改变查询

考虑一下这个场景:我有两个带有机器名的实体类型:tincan语句和tincan_agents

实体上的5个实体引用字段

其中4个是常规实体参考字段,第5个(tincan_object)是multi-entity-type参考字段,每个参考字段引用’Agent’类型的实体。

tincan_object参考字段可以引用代理和活动(第三种实体类型)。代理具有属性object_type,可以是代理或组。

我想在任何参考字段中找到任何引用几个可能的代理之一的Statement。我们需要在fieldConditions之间使用OR运算符,但我们还需要检查multi-entity类型引用字段的object_type,并确保它是两种可能之一。

下面的代码表示最简单的可能,在我们的解决方案中,查询有许多其他条件,字段等…所以代码不需要依赖条件的顺序,或者即使所有这些字段都被查询。

    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'tincan_statement');

    $all_agents = array(4,10); //entity_ids to search for
    $query->addTag('tincan_statement_get_agents');
    $query->fieldCondition('tincan_actor', 'target_id', $all_agents, 'IN'); 
    //need OR between fields conditions
    $query->fieldCondition('tincan_authority', 'target_id', $all_agents, 'IN');
//need OR between fields conditions
    $query->fieldCondition('tincan_instructor', 'target_id', $all_agents, 'IN');
//need OR between fields conditions
    $query->fieldCondition('tincan_team', 'target_id', $all_agents, 'IN');
//need OR between fields conditions
//but then nested in the OR structure we need an AND for two columns of the multientity type reference field tincan_object
    $query->fieldCondition('tincan_object', 'target_id', $all_agents, 'IN');
    $query->fieldCondition('tincan_object', 'object_type', array('Agent', 'Group'), 'IN');
    $results = $query->$execute();

解决方案:请注意上面的EntityFieldQuery

 $query->addTag('tincan_statement_get_agents');

这标记查询,允许实现 hook_query_TAG_alter()

/**
 * Implements hook_query_TAG_alter()
 * alters the query for finding agents with or without the related_agents flag
 * used for Statement API Get processor EntityFieldQuery
 */
function tincan_lrs_query_tincan_statement_get_agents_alter(QueryAlterableInterface $query) {
  //need to or the search for all the fields (actor, object, authority, instructor, team)
  // the object_type of the object field needs to be Agent OR Group

  $conditions =& $query->conditions();
  // dsm($conditions);  //dsm() is your friend! comes with devel module
  $agent_grouping_condition = db_or(); 
  $object_parameters = array();
  $x = 0;
  foreach ($conditions as $key => $condition) {
    if (is_numeric($key) && isset($condition['field']) && is_scalar($condition['field'])) {
      if ( (strpos($condition['field'], 'tincan_object_object_type') !== FALSE  ||
          strpos($condition['field'], 'tincan_object_target_id') !== FALSE ) && $condition['operator'] == 'IN') {
  //u
            unset($conditions[$key]);
            $object_parameters[$x]['field'] = $condition['field'];
            $object_parameters[$x]['value'] = $condition['value'];
            $object_parameters[$x]['operator'] = $condition['operator'];
            $x += 1;
          }

       if(strpos($condition['field'], 'tincan_actor_target_id') !== FALSE ||
          strpos($condition['field'], 'tincan_instructor_target_id') !== FALSE ||
          strpos($condition['field'], 'tincan_team_target_id') !== FALSE ||
          strpos($condition['field'], 'tincan_authority_target_id') !== FALSE ) {
            unset($conditions[$key]);
            $agent_grouping_condition->condition($condition['field'], $condition['value'], $condition['operator']);

      } 
    }
  }

  // create new AND condition to nest in our OR condition set for the object parameters
  $object_condition = db_and();
  foreach($object_parameters as $key => $param) {
    $object_condition->condition($param['field'], $param['value'], $param['operator']);
  }

  $agent_grouping_condition->condition($object_condition);

  $query->condition($agent_grouping_condition);

  //By default EntityFieldQuery uses inner joins, change to left
  $tables =& $query->getTables();

  foreach($tables as $key => $table) {
    if (strpos($key, 'field_data_tincan_object') !== FALSE ||
        strpos($key, 'field_data_tincan_actor') !== FALSE ||
        strpos($key, 'field_data_tincan_authority') !== FALSE ||
        strpos($key, 'field_data_tincan_instructor') !== FALSE ||
        strpos($key, 'field_data_tincan_team') !== FALSE ) {
          if(!is_null($table['join type'])) {
            $tables[$key]['join type'] = 'LEFT';
          }
    }
  }

}

第四种思路

您可以担心’t I’m,EntityFieldQuery类本身不支持OR。

其中一种方法可能是使用->addTag()向查询添加标记,然后实现hook_query_TAG_alter()以手动更改包含该标记的查询的查询内部结构。

这样做,您将能够遍历现有条件并进行必要的更改以添加OR逻辑。虽然这不是一个很好的方式;你可以找到example here

第五种思路

OP想要查询日期为null或大于x的实体,我想查询没有定义语言的节点或用户的语言。 addTag()是添加实际OR语句的最佳解决方案,但在我的情况下会有点过分。我可以通过使用以下方法查找数组中的语言属性来完成我非常简单的OR:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');

参考资料

本文由朵颐IT整理自网络, 文章地址: https://duoyit.com/article/2167.html,转载请务必附带本地址声明。