Utilizzo di OR con EntityFieldQuery


26

Non ho mai avuto la necessità di farlo prima di oggi, ma non sembra che tu possa fare query OR EntityFieldQuery, poiché db_orviene utilizzato per query selezionate.

Un esempio potrebbe arrivare a tutte le entità che hanno un campo data in cui il valore è nullo o dopo oggi.

Mi sto perdendo qualcosa o qualche trucco o semplicemente non è supportato?


Puoi anche dividere una query in due, eseguirli e quindi unire i risultati.
Vadym Myrgorod,

L'impatto sulle prestazioni di questo è piuttosto orribile se le query o la quantità di dati sono anche lontanamente più grandi.
Tommi Forsström,

1
Questo è vecchio ma in alto nei miei risultati di Google - va notato che puoi usare orConditionGroup per questo in Drupal 8.
ognockocaten

Risposte:


22

Ho visto una soluzione a questo problema . L'idea è quella di utilizzare addTag()in query e implementare hook_query_TAG_alter(), dove hai un buon vecchio SelectQueryoggetto.


Proporrei di selezionarlo come la risposta giusta. Il post sul blog fornisce un metodo per aggiungere la condizionalità OR a EntityFieldQueries. L'unico problema è che in realtà costruisci la dipendenza SQL con quel metodo, che è un po 'contro l'intero punto degli EFQ, ma almeno fa il lavoro. Grazie per l'ottimo link @Michael.
Tommi Forsström,

2
Dal momento che questa è una risposta della comunità, e la maggior parte è costituita da un collegamento esterno, penso che il codice, o almeno alcuni dei contenuti dell'articolo, dovrebbero essere inclusi in questa risposta. Perché i collegamenti muoiono. Meta Stack Scambio discussione su questo argomento
D. Visser

L'articolo originale è piuttosto lungo e l'idea può essere riassunta come "usa addTag () nelle query e implementa hook_query_TAG_alter ()". Dopodiché la domanda è stata ridotta a "Come usare OR con oggetto SelectQuery" che è noto soggetto.
Michael,

Consiglio vivamente di convertire l'EFQ in una query di selezione regolare in questo scenario. Attaccare con l'EFQ e usare le modifiche basate su tag per fare confusione con ciò che produce è orribile al confronto.
phils,

12

È possibile sublassare EntityFieldQuerye sovrascrivere alcuni metodi.

Le condizioni che vengono aggiunte a un oggetto di classe EntityFieldQuery(ad esempio una condizione di proprietà) vengono aggiunte a un array.

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

Quando viene creata la query, quell'array viene quindi utilizzato in un ciclo simile al seguente (il codice è presente in EntityFieldQuery :: propertyQuery () ):

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

$select_querycontiene il valore restituito da una chiamata a db_select().


5

Non puoi temere, gli OR non sono supportati nativamente dalla EntityFieldQueryclasse.

In un modo potrebbe essere aggiungere un tag alla query con con ->addTag(), quindi implementare hook_query_TAG_alter()per modificare manualmente la struttura interna della query per le query che contengono quel tag.

In questo modo sarai in grado di scorrere le condizioni esistenti e apportare le modifiche necessarie per aggiungere la tua ORlogica. Non è un bel modo di farlo però; puoi trovare un esempio qui .


5

Non è necessario dividere le query in 2 e unirle o simili. Devo solo modificare la query

Considera lo scenario: avevo 2 tipi di entità con nomi di macchine: istruzioni tincan e tincan_agents

5 campi di riferimento dell'entità sull'entità

4 di questi sono campi di riferimento di entità regolari e il quinto (tincan_object) è un campo di riferimento di tipo multientità, ogni campo di riferimento fa riferimento a entità di tipo "Agente".

Il campo di riferimento tincan_object può fare riferimento ad agenti e attività (un tipo di terza entità). Un agente ha una proprietà tipo_oggetto, che può essere agente o gruppo.

Voglio trovare qualsiasi Dichiarazione che faccia riferimento a uno dei numerosi possibili agenti, in uno qualsiasi dei campi di riferimento. Abbiamo bisogno di un operatore OR tra le condizioni del campo, ma dobbiamo anche controllare il tipo_oggetto del campo di riferimento del tipo multi-entità e assicurarci che sia una delle due possibilità.

Il codice seguente rappresenta il più semplice possibile, nella nostra soluzione la query presentava molte altre condizioni, campi, ecc ... quindi il codice doveva non contare sull'ordine delle condizioni, o anche se tutti questi campi fossero stati interrogati.

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

Soluzione: notare in EntityFieldQuery sopra

 $query->addTag('tincan_statement_get_agents');

Questo tag la query, consentendo l'implementazione di 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

L'OP vuole interrogare entità con data null O maggiore di x, volevo interrogare nodi senza una lingua definita O la lingua dell'utente. addTag()è la soluzione migliore per aggiungere un'istruzione OR effettiva, ma sarebbe eccessivo nel mio caso. Il mio OR molto semplice può essere realizzato cercando la proprietà del linguaggio in un array usando:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.