Używanie OR z EntityFieldQuery

26

Nigdy wcześniej nie musiałem tego robić, ale nie wydaje się, że możesz tworzyć zapytania OR EntityFieldQuery, ponieważ db_orjest ono używane do wybranych zapytań.

Przykładem może być wszystkie podmioty, które mają pole daty, w którym wartość jest zerowa lub po dniu dzisiejszym.

Czy brakuje mi czegoś lub jakiejś sztuczki, czy to po prostu nie jest obsługiwane?

googletorp
źródło
Możesz także podzielić jedno zapytanie na dwa, uruchomić je, a następnie dołączyć wyniki.
Vadym Myrgorod,
Wpływ tego na wydajność jest okropny, jeśli zapytania lub ilość danych są nawet znacznie większe.
Tommi Forsström
1
To jest stare, ale wysoko w moich wynikach Google - należy zauważyć, że możesz do tego użyć orConditionGroup w Drupal 8.
ognockocaten

Odpowiedzi:

22

Widziałem rozwiązanie tego problemu . Chodzi o to, aby użyć addTag()w zapytaniu i implementacji hook_query_TAG_alter(), gdzie masz dobry stary SelectQueryobiekt.

Michał
źródło
Proponuję wybrać to jako właściwą odpowiedź. Wpis na blogu zawiera metodę dodawania warunkowości OR do EntityFieldQueries. Jedynym problemem jest to, że faktycznie budujesz zależność SQL za pomocą tej metody, co jest w pewnym sensie sprzeczne z całym punktem EFQ, ale przynajmniej wykonuje zadanie. Dzięki za dobry link @ Michael.
Tommi Forsström,
2
Ponieważ jest to odpowiedź społeczności, a większość z nich składa się z zewnętrznego linku, wydaje mi się, że kod lub przynajmniej część artykułu powinna zostać zawarta w tej odpowiedzi. Ponieważ linki umierają. Dyskusja Meta StackExchange na ten temat
D. Visser
Oryginalny artykuł jest dość długi, a pomysł można streścić jako „użyj zapytania addTag () w zapytaniu i zaimplementuj hook_query_TAG_alter ()”. Następnie pytanie zostało zredukowane do „Jak używać OR z obiektem SelectQuery”, co jest znanym tematem.
Michael
W tym scenariuszu zdecydowanie zaleciłbym konwersję EFQ na zwykłe zapytanie wyboru. Trzymanie się EFQ i używanie zmian opartych na tagach, aby zadzierać z tym, co produkuje, jest okropne w porównaniu.
phils
12

Możesz sublassować EntityFieldQueryi zastąpić niektóre metody.

Warunki, które są dodawane do obiektu klasy EntityFieldQuery(np. Warunek właściwości) są dodawane do tablicy.

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

Po zbudowaniu zapytania tablica ta jest następnie używana w pętli podobnej do poniższej (kod jest obecny w EntityFieldQuery :: propertyQuery () ):

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

$select_queryzawiera wartość zwróconą z wywołania do db_select().

kiamlaluno
źródło
5

Nie obawiam się, że OR nie są natywnie wspierane przez EntityFieldQueryklasę.

Jednym sposobem może być dodanie znacznika do zapytania za pomocą przy pomocy ->addTag(), a następnie zaimplementowanie hook_query_TAG_alter()ręcznej zmiany wewnętrznej struktury zapytania dla zapytań zawierających ten znacznik.

W ten sposób będziesz mógł przejrzeć istniejące warunki i dokonać niezbędnych zmian, aby dodać swoją ORlogikę. Nie jest to jednak ładny sposób na zrobienie tego; możesz znaleźć przykład tutaj .

Clive
źródło
5

Nie trzeba dzielić zapytań na 2 i łączyć ani nic w tym rodzaju. Wystarczy zmienić zapytanie

Rozważmy scenariusz: miałem 2 typy jednostek o nazwach komputerów: instrukcje tincan i tincan_agents

5 pól referencyjnych encji na encji

4 z nich to zwykłe pola referencyjne encji, a piąte (tincan_object) to pole referencyjne typu wiele encji, każde pole referencyjne odnosi się do encji typu „Agent”.

Pole referencyjne tincan_object może odwoływać się do Agentów i Działań (trzeci typ encji). Agent ma właściwość typ_obiektu, którą może być Agent lub Grupa.

Chcę znaleźć dowolną instrukcję, która odwołuje się do jednego z kilku możliwych agentów, w dowolnym polu referencyjnym. Potrzebujemy operatora OR między fieldConditions, ale musimy również sprawdzić typ_obiektu pola referencyjnego typu wielu encji i upewnić się, że jest to jedna z dwóch możliwości.

Poniższy kod reprezentuje najprostszy możliwy, w naszym rozwiązaniu zapytanie zawierało wiele innych warunków, pól itp., Więc kod nie musiał liczyć na kolejność warunków, a nawet jeśli wszystkie te pola były sprawdzane.

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

Rozwiązanie: Uwaga w powyższym EntityFieldQuery

 $query->addTag('tincan_statement_get_agents');

To taguje zapytanie, umożliwiając implementację 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
źródło
2

OP chce zapytać o jednostki o dacie zerowej LUB większej niż x, chciałem zapytać o węzły bez zdefiniowanego języka LUB języka użytkownika. addTag()jest najlepszym rozwiązaniem do dodania rzeczywistej instrukcji OR, ale w moim przypadku byłoby to przesadą. Moje bardzo proste OR można osiągnąć, wyszukując właściwość języka w tablicy za pomocą:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
lmeurs
źródło