Використання АБО з EntityFieldQuery


26

У мене ніколи не було цього робити до сьогодні, але, схоже, не можна робити запити АБО EntityFieldQuery, оскільки db_orвикористовується для вибору запитів.

Приклад можна отримати до всіх об'єктів, у яких є поле дати, де значення є нульовим або після сьогоднішнього дня.

Я щось пропускаю чи якусь хитрість чи це просто не підтримується?


Ви також можете розділити один запит на два, запустити їх і потім об'єднати результати.
Вадим Миргород

Вплив на ефективність цього досить жахливий, якщо запитів чи кількості даних ще віддалено більше.
Tommi Forsström

1
Це старе, але високо в моїх результатах Google - слід зазначити, що ви можете використовувати orConditionGroup для цього в Drupal 8.
ognockocaten

Відповіді:


22

Я бачив вирішення цієї проблеми . Ідея полягає у використанні addTag()в запиті та реалізації hook_query_TAG_alter(), де у вас є добрий старий SelectQueryоб'єкт.


Я б запропонував вибрати це як правильну відповідь. Повідомлення в блозі надає спосіб додавання АБО обумовленості умовами EntityFieldQueries. Єдина проблема полягає в тому, що ви фактично будуєте залежність від SQL за допомогою цього методу, який є протилежним до всієї точки EFQ, але принаймні виконує цю роботу. Дякуємо за гарне посилання @Michael.
Tommi Forsström

2
Оскільки це відповідь громади, і більшість її складається із зовнішнього посилання, я вважаю, що код або хоча б частина змісту статті має бути включений до цієї відповіді. Тому що посилання вмирають. Мета StackЗмінити дискусію на цю тему
D. Visser

Оригінальна стаття досить довга, і ідея може бути узагальнена як "використання addTag () у запиті та реалізація moy_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

Ви можете не боятись, АБО не підтримується EntityFieldQueryкласом.

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

Роблячи це, ви зможете переглядати існуючі умови та внести необхідні зміни, щоб додати свою ORлогіку. Це не дуже гарний спосіб зробити це, хоча; ви можете знайти приклад тут .


5

Не потрібно ділити запити на 2 і об’єднувати чи щось подібне. Просто потрібно змінити запит

Розглянемо сценарій: у мене було 2 типи сутностей з іменами машин: твердження tincan та tincan_agents

5 посилальних полів на сутність

4 з них є регулярними посиланнями сутностей, а 5-е (tincan_object) є посилальним полем мульти-сутності, кожне посилання посилається на сутності типу "Агент".

Посилання tincan_object може посилатися на Агенти та Діяльність (тип третього об'єкта). Агент має властивість 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');

Це позначає тег запиту, що дозволяє реалізовувати прив'язку_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

ОП хоче запитувати суб’єкти з датою нульової АБО більше x, я хотів запитувати вузли, не визначені мовою АБО мовою користувача. addTag()є найкращим рішенням для додавання фактичного оператора АБО, але в моєму випадку буде надмірне. Моє дуже просте АБО можна досягти, шукаючи властивість мови в масиві, використовуючи:

$query->propertyCondition('language', array($GLOBALS['language']->language, LANGUAGE_NONE), 'IN');
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.