Verwenden von OR mit EntityFieldQuery

26

Ich hatte bis heute noch nie die Notwendigkeit, dies zu tun, aber es scheint nicht so, als könnten Sie mit ODER-Abfragen arbeiten EntityFieldQuery, da dies db_orfür ausgewählte Abfragen verwendet wird.

Ein Beispiel bezieht sich auf alle Entitäten mit einem Datumsfeld, deren Wert null oder später als heute ist.

Fehlt mir etwas oder ein Trick oder wird dies einfach nicht unterstützt?

googletorp
quelle
Sie können auch eine Abfrage in zwei aufteilen, sie ausführen und dann die Ergebnisse zusammenführen.
Vadym Myrgorod
Die Auswirkungen auf die Leistung sind ziemlich schrecklich, wenn die Abfragen oder die Datenmenge auch nur annähernd größer sind.
Tommi Forsström
1
Dies ist alt, aber in meinen Google-Ergebnissen weit oben - es sollte beachtet werden, dass Sie die orConditionGroup für diese in Drupal 8 verwenden können.
ognockocaten

Antworten:

22

Ich habe eine Lösung für dieses Problem gesehen . Die Idee ist, addTag()in Query und Implement zu verwenden hook_query_TAG_alter(), wo Sie gute alte SelectQueryObjekte haben.

Michael
quelle
Ich würde vorschlagen, dies als die richtige Antwort auszuwählen. Der Blogbeitrag bietet eine Methode zum Hinzufügen von ODER-Bedingungen zu EntityFieldQueries. Das einzige Problem ist, dass Sie mit dieser Methode tatsächlich eine SQL-Abhängigkeit aufbauen, die dem Sinn der EFQs in nichts nachsteht, aber zumindest die Arbeit erledigt. Danke für den guten Link @Michael.
Tommi Forsström
2
Da es sich um eine Community-Antwort handelt und der Großteil aus einem externen Link besteht, sollte der Code oder zumindest ein Teil des Inhalts des Artikels in dieser Antwort enthalten sein. Weil Links sterben. Meta StackExchange Diskussion zu diesem Thema
D. Visser
Der ursprüngliche Artikel ist ziemlich lang und die Idee kann als "use addTag () in query and implement hook_query_TAG_alter ()" zusammengefasst werden. Danach wurde die Frage auf "Verwendung von OR mit SelectQuery-Objekten" reduziert, was ein bekanntes Thema ist.
Michael
Ich würde dringend empfehlen, den EFQ in diesem Szenario in eine reguläre Auswahlabfrage umzuwandeln. Sich an den EFQ zu halten und tagbasierte Änderungen zu verwenden, um mit dem, was er produziert, durcheinander zu bringen, ist im Vergleich schrecklich.
Phils
12

Sie können EntityFieldQueryeinige Methoden unterklassifizieren und überschreiben.

Die Bedingungen, die einem Objekt der Klasse hinzugefügt werden EntityFieldQuery(z. B. eine Eigenschaftsbedingung), werden einem Array hinzugefügt.

  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;
  }

Wenn die Abfrage erstellt wird, wird dieses Array in einer Schleife verwendet, die der folgenden ähnelt (der Code ist in EntityFieldQuery :: propertyQuery () enthalten ):

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

$select_queryEnthält den von einem Aufruf an zurückgegebenen Wert db_select().

kiamlaluno
quelle
5

Sie können keine Angst haben, ORs werden von der EntityFieldQueryKlasse nicht nativ unterstützt .

Eine Möglichkeit besteht darin, der Abfrage mit ein Tag hinzuzufügen ->addTag()und dann zu implementieren hook_query_TAG_alter(), um die interne Struktur der Abfrage für Abfragen, die dieses Tag enthalten, manuell zu ändern.

Auf diese Weise können Sie die vorhandenen Bedingungen durchlaufen und die erforderlichen Änderungen vornehmen, um Ihre ORLogik hinzuzufügen . Es ist jedoch keine schöne Methode, dies zu tun. Ein Beispiel finden Sie hier .

Clive
quelle
5

Keine Notwendigkeit, Abfragen in 2 zu teilen und zusammenzuführen oder ähnliches. Müssen nur die Abfrage ändern

Stellen Sie sich das Szenario vor: Ich hatte zwei Entitätstypen mit Maschinennamen: tincan-Anweisungen und tincan_agents

5 Entitätsreferenzfelder auf der Entität

4 davon sind reguläre Entitätsreferenzfelder und das fünfte (tincan_object) ist ein Referenzfeld mit mehreren Entitäten, wobei jedes Referenzfeld auf Entitäten vom Typ 'Agent' verweist.

Das Referenzfeld tincan_object kann auf Agenten und Aktivitäten verweisen (ein dritter Entitätstyp). Ein Agent hat eine Eigenschaft object_type, die entweder Agent oder Group sein kann.

Ich möchte eine Aussage finden, die auf einen von mehreren möglichen Agenten in einem der Referenzfelder verweist. Wir benötigen einen OR-Operator zwischen den fieldConditions, müssen aber auch den object_type des Referenzfelds mit mehreren Entitätstypen überprüfen und sicherstellen, dass es sich um eine von zwei Möglichkeiten handelt.

Der folgende Code stellt den einfachsten möglichen Code dar. In unserer Lösung hatte die Abfrage viele andere Bedingungen, Felder usw., sodass der Code nicht mit der Reihenfolge der Bedingungen gerechnet werden musste oder auch wenn alle diese Felder abgefragt wurden.

    $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();

Lösung: Beachten Sie in der obigen EntityFieldQuery

 $query->addTag('tincan_statement_get_agents');

Dies markiert die Abfrage und ermöglicht die Implementierung von 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';
          }
    }
  }

}
Jackrabbithanna
quelle
2

Das OP möchte nach Entitäten mit dem Datum null ODER größer als x suchen. Ich möchte nach Knoten ohne definierte Sprache ODER nach der Sprache des Benutzers suchen. addTag()ist die beste Lösung für das Hinzufügen einer tatsächlichen ODER-Anweisung, wäre aber in meinem Fall übertrieben. Mein sehr einfaches ODER kann erreicht werden, indem die Spracheigenschaft in einem Array nachgeschlagen wird mit:

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