Использование ИЛИ с EntityFieldQuery

26

У меня никогда не было необходимости делать это до сегодняшнего дня, но, похоже, вы не можете делать OR-запросы с помощью EntityFieldQuery, так db_orкак используется для запросов select.

Примером могут быть все объекты, имеющие поле даты, в котором значение равно нулю или после сегодняшнего дня.

Я что-то упустил или какой-то трюк, или это просто не поддерживается?

googletorp
источник
Вы также можете разделить один запрос на два, выполнить их и затем объединить результаты.
Вадим Миргород
Влияние на производительность это довольно ужасно, если запросы или объем данных даже отдаленно больше.
Томми Форсстрем
1
Это старый, но высоко в моих результатах Google - следует отметить, что вы можете использовать orConditionGroup для этого в Drupal 8.
ognockocaten

Ответы:

22

Я видел решение этой проблемы . Идея заключается в использовании addTag()в запросе и реализации hook_query_TAG_alter(), где у вас есть старый добрый SelectQueryобъект.

Майкл
источник
Я бы предложил выбрать это как правильный ответ. Сообщение в блоге предоставляет метод для добавления условий ИЛИ в EntityFieldQueries. Единственная проблема заключается в том, что вы фактически строите зависимость SQL с помощью этого метода, что в некоторой степени противоречит смыслу EFQ, но, по крайней мере, выполняет свою работу. Спасибо за хорошую ссылку @Michael.
Томми Форсстрем
2
Поскольку это ответ сообщества, и большая его часть состоит из внешней ссылки, я чувствую, что код или, по крайней мере, часть содержимого статьи должны быть включены в этот ответ. Потому что ссылки умирают. Обсуждение Meta StackExchange на эту тему
D. Visser
Оригинальная статья довольно длинная, и идея может быть обобщена как «используйте addTag () в запросе и реализуйте hook_query_TAG_alter ()». После этого вопрос был сокращен до «Как использовать ИЛИ с объектом SelectQuery», который является известным предметом.
Майкл
Я настоятельно рекомендую преобразовать 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

Боюсь, ты не можешь, классы не поддерживают OR EntityFieldQuery.

Одним из способов решения этой проблемы может быть добавление тега к запросу с помощью ->addTag(), а затем реализация hook_query_TAG_alter()для изменения внутренней структуры запроса вручную для запросов, содержащих этот тег.

Делая это, вы сможете пройти через существующие условия и внести необходимые изменения, чтобы добавить свою ORлогику. Это не очень хороший способ сделать это; Вы можете найти пример здесь .

Клайв
источник
5

Не нужно разбивать запросы на 2 и объединять или что-то в этом роде. Просто нужно изменить запрос

Рассмотрим сценарий: у меня было 2 типа сущностей с именами машин: операторы tincan и tincan_agents

5 справочных полей объекта на объекте

4 из них являются ссылочными полями обычных сущностей, а 5-е (tincan_object) является ссылочным полем с несколькими сущностями, каждое ссылочное поле ссылается на сущности типа «Агент».

Ссылочное поле tincan_object может ссылаться на Агенты и Действия (третий тип сущности). Агент имеет свойство object_type, которое может быть агентом или группой.

Я хочу найти любое Заявление, которое ссылается на одного из нескольких возможных Агентов, в любом из полей ссылки. Нам нужен оператор OR между fieldConditions, но мы также должны проверить 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';
          }
    }
  }

}
jackrabbithanna
источник
2

ОП хочет запросить сущности с нулевой датой ИЛИ больше, чем х, я хотел запросить узлы без определенного языка ИЛИ язык пользователя. addTag()является лучшим решением для добавления фактического оператора OR, но в моем случае это было бы излишним. Мое очень простое ИЛИ может быть достигнуто путем поиска свойства языка в массиве с помощью:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
lmeurs
источник