EntityFieldQueryでORを使用する


26

今日までこれを行う必要はありませんでしたが、選択クエリに使用されているためEntityFieldQuery、でORクエリを作成できるようには見えませんdb_or

例では、値がnullまたは今日以降の日付フィールドを持つすべてのエンティティを取得します。

私は何かまたは何かトリックを逃していますか、これは単にサポートされていませんか?


1つのクエリを2つに分割して実行し、結果を結合することもできます。
ヴァディムマーゴロド

クエリまたはデータの量がリモートでさらに大きい場合、これのパフォーマンスへの影響はかなり恐ろしいです。
トミフォルストローム

1
あなたがDrupalの8で、このためにorConditionGroupを使用できることに留意すべきである-これは古いが、私のGoogleの検索結果でハイアップしている
ognockocaten

回答:


22

私はこの問題の解決策を見てきました。アイデアはaddTag()queryで使用しhook_query_TAG_alter()、古き良きSelectQueryオブジェクトがあるところで実装します。


これを正しい答えとして選択することを提案します。ブログの投稿では、EntityFieldQueriesにOR条件を追加する方法を提供しています。唯一の問題は、実際にそのメソッドを使用してSQL依存関係を構築することです。これは、EFQの全ポイントに反しますが、少なくとも仕事は完了します。良いリンク@Michaelに感謝します。
トミフォルストローム

2
これはコミュニティの回答であり、そのほとんどが外部リンクで構成されているため、コード、または少なくとも記事の内容の一部をこの回答に含める必要があると思います。リンクが死ぬからです。このトピックに関するメタStackExchangeディスカッション
D.フィッセル

元の記事はかなり長く、アイデアは「クエリでaddTag()を使用してhook_query_TAG_alter()を実装する」と要約できます。その後、質問は既知の主題である「SelectQueryオブジェクトでORを使用する方法」に縮小されました。
マイケル

このシナリオでは、EFQを通常の選択クエリに変換することを強くお勧めします。EFQに固執し、タグベースの変更を使用してEFQが生成するものを混乱させることは、比較すると恐ろしいことです。
phils

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

恐れることはできません。ORはEntityFieldQueryクラスによってネイティブにサポートされていません。

方法の1つは、を使用してクエリにタグを追加し、そのタグを含むクエリのクエリの内部構造を手動で変更するように->addTag()実装hook_query_TAG_alter()することです。

これを行うと、既存の条件をループして、ORロジックを追加するために必要な変更を加えることができます。しかし、それを行うためのきれいな方法ではありません。ここで例を見つけることができます。


5

クエリを2つに分割してマージする必要はありません。クエリを変更するだけです

シナリオを考えてみましょう:マシン名を持つ2つのエンティティタイプ:tincanステートメントとtincan_agentsがありました

エンティティ上の5つのエンティティ参照フィールド

それらの4つは通常のエンティティ参照フィールドであり、5番目(tincan_object)は複数エンティティタイプの参照フィールドです。各参照フィールドは「Agent」タイプのエンティティを参照します。

tincan_object参照フィールドは、エージェントとアクティビティ(3番目のエンティティタイプ)を参照できます。エージェントにはプロパティobject_typeがあり、これはエージェントまたはグループのいずれかです。

参照フィールドのいずれかで、いくつかの可能なエージェントの1つを参照するステートメントを見つけたいです。fieldConditionsの間にOR演算子が必要ですが、複数エンティティタイプの参照フィールドのobject_typeを確認し、2つの可能性のいずれかであることを確認する必要もあります。

以下のコードは可能な限り単純なコードを示しています。このソリューションでは、クエリには他の多くの条件、フィールドなどが含まれていたため、コードは条件の順序に依存しないようにする必要がありました。

    $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は、配列を使用して言語プロパティを検索することで実現できます。

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.