结合使用OR和EntityFieldQuery


26

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

一个示例将涉及具有日期字段(该字段的值为空或今天之后)的所有实体。

我是否缺少某些东西或技巧,或者根本不支持此方法?


您还可以将一个查询拆分为两个查询,然后运行它们,然后合并结果。
Vadym Myrgorod

如果查询或数据量远大于此,那么对性能的影响将非常可怕。
TommiForsström,2012年

1
这是旧的,但高在我的谷歌的结果-它应该指出的是,你可以使用orConditionGroup为此在Drupal 8
ognockocaten

Answers:


22

我已经看到了这个问题解决方案。这个想法是用addTag()在查询和实现中hook_query_TAG_alter(),在这里您有很好的旧SelectQuery对象。


我建议选择这个作为正确的答案。博客文章提供了一种向EntityFieldQueries添加OR条件的方法。唯一的问题是,您实际上使用该方法构建了SQL依赖关系,这与EFQ的全部观点背道而驰,但至少可以完成工作。感谢@Michael的良好链接。
TommiForsström'7年

2
由于这是一个社区答案,并且大多数都包含一个外部链接,因此我认为该答案中应包括代码或至少文章的某些内容。因为链接确实消失了。Meta StackExchange对此主题的讨论
D. Visser

原始文章相当长,其思想可以概括为“在查询中使用addTag()并实现hook_query_TAG_alter()”。之后,问题被简化为“如何与SelectQuery对象一起使用OR”,这是已知的主题。
迈克尔

在这种情况下,我强烈建议您将EFQ转换为常规选择查询。相比之下,坚持使用EFQ并使用基于标签的更改来弄乱它产生的内容是可怕的。
菲尔

12

您可以细分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()


5

您不必担心,EntityFieldQuery该类本身不支持OR 。

绕过它的一种方法可能是使用,将标签添加到查询中->addTag(),然后实施hook_query_TAG_alter()以针对包含该标签的查询手动更改查询的内部结构。

这样做,您将能够遍历现有条件并进行必要的更改以添加OR逻辑。不过,这不是一个好方法。你可以在这里找到一个例子


5

无需将查询分为2个并合并或类似的东西。只需更改查询

考虑这种情况:我有两种带有机器名称的实体类型:tincan语句和tincan_agents

实体上的5个实体参考字段

其中的4个是常规实体参考字段,第5个(tincan_object)是一个多实体类型参考字段,每个参考字段都参考一个类型为“代理”的实体。

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

我想在任何参考字段中找到任何引用几个可能的代理之一的语句。我们在fieldConditions之间需要一个OR运算符,但是我们还需要检查多实体类型引用字段的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';
          }
    }
  }

}

2

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

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.