Рейтинг:0

Выполнение entityQuery с несколькими условиями полей в полях, на которые ссылаются

флаг um

У меня есть тип узла статьи с полем, ссылающимся на связанные статьи. Если статья имеет менее 4 ссылок на это поле, мне нужно искать другие статьи, которые

  • не являются текущим узлом
  • были обновлены менее 2 лет назад
  • поставить галочку "избранное"
  • поделиться хотя бы одним идентификатором культуры (ссылка на таксономию) И хотя бы одним идентификатором темы (ссылка на таксономию)

И мне действительно интересно, как выполнить эту последнюю потребность... На основе Выполнение запроса с условием поля объекта с несколькими значениями, я написал следующий код:

<?php

пространство имен Drupal\my_module\Preprocess;

используйте Drupal\Core\DependencyInjection\ContainerInjectionInterface;
используйте Drupal\Core\Entity\EntityTypeManagerInterface;
используйте Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Тип узла предварительной обработки Статья.
 */
класс MyArticle реализует ContainerInjectionInterface {

  константа MAX_RELATED_ARTICLES = 4;

  /**
   * Хранение терминов таксономии.
   *
   * @var \Drupal\node\NodeStorage
   */
  защищенное $nodeStorage;

  /**
   * Конструктор класса.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   * Менеджер типов объектов.
   */
  общедоступная функция __construct (EntityTypeManagerInterface $entity_type_manager) {
    $this->nodeStorage = $entity_type_manager->getStorage('узел');
  }

  /**
   * {@inheritdoc}
   */
  общедоступная статическая функция create(ContainerInterface $container) {
    вернуть новую статику(
      $container->получить('entity_type.manager')
    );
  }

  /**
   * Статья о типе узла предварительной обработки.
   *
   * массив @param $переменные
   * Массив переменных, используемых для рендеринга узла.
   */
  предварительная обработка публичной функции (массив и переменные $) {
    если ('полный' === $variables['view_mode']) {
      $this->preprocessFull($variables);
    }
  }

  /**
   * Предварительная обработка статьи типа узла в режиме просмотра full.
   *
   * массив @param $переменные
   * Массив переменных, используемых для рендеринга узла.
   */
  защищенная функция preprocessFull (массив и переменные $) {
    $узел = $переменные['узел'];

    $related_articles = $node->get('related_articles')->referencedEntities();
    $themes = $node->get('themes')->referencedEntities();
    $cultures = $node->get('cultures')->referencedEntities();
    $themes_ids = $cultures_ids = [];
    foreach ($темы как $term) {
      $themes_ids[] = $term->id();
    }
    foreach ($культуры как $term) {
      $cultures_ids[] = $term->id();
    }
    $nb_related_articles = количество($related_articles);

    если ($nb_related_articles < self::MAX_RELATED_ARTICLES) {
      $limit = self::MAX_RELATED_ARTICLES - $nb_related_articles;
      $changed_boundary = strtotime('-2 года');
      $query = $this->nodeStorage->getQuery()
        ->условие('тип', 'статья')
        ->условие('статус', 1)
        ->условие('nid', $node->id(), '<>')
        ->условие('любимое', 1)
        ->условие('изменено', $changed_boundary, '>=')
        ->диапазон(0, $предел);
      $or_themes = $or_cultures = $query->orConditionGroup();
      foreach ($themes_ids как $tid) {
        $or_themes->условие('культуры', $tid);
      }
      foreach ($cultures_ids как $tid) {
        $or_cultures->условие('темы', $tid);
      }
      $and = $query->andConditionGroup()
        -> условие ($ or_themes);
      $запрос->условие($и);
      $and = $query->andConditionGroup()
        ->условие($или_культуры);
      $запрос->условие($и);
      $node_ids = $запрос->выполнить();
    }
  }

}

который дает мне следующий запрос:

ВЫБЕРИТЕ «base_table». «vid» КАК «vid», «base_table». «nid» КАК «nid»
ОТ "узла" "базовая_таблица"
ВНУТРЕННЕЕ СОЕДИНЕНИЕ "node_field_data" "node_field_data" ON "node_field_data"."nid" = "base_table"."nid"
ВНУТРЕННЕЕ СОЕДИНЕНИЕ "node__favorite" "node__favorite" ON "node__favorite"."entity_id" = "base_table"."nid"
ЛЕВОЕ СОЕДИНЕНИЕ "node__cultures" "node__cultures" ON "node__cultures"."entity_id" = "base_table"."nid"
LEFT JOIN "node__themes" "node__themes" ON "node__themes"."entity_id" = "base_table"."nid"
ЛЕВОЕ СОЕДИНЕНИЕ "node__cultures" "node__cultures_2" ON "node__cultures_2"."entity_id" = "base_table"."nid"
LEFT JOIN "node__themes" "node__themes_2" ON "node__themes_2"."entity_id" = "base_table"."nid"
ГДЕ ("node_field_data". "тип" = 'статья')
И ("node_field_data". "status" = 'NODE_PUBLISHED')
И ("node_field_data"."nid" <> '19533')
И ("node__favorite"."favorite_value" = '1')
И ("node_field_data". "изменено" >= '1583280325')
И (("node__cultures"."cultures_target_id" = '5077') или ("node__themes"."themes_target_id" = '42') или ("node__themes"."themes_target_id" = '38'))
И (("node__cultures_2"."cultures_target_id" = '5077') или ("node__themes_2"."themes_target_id" = '42') или ("node__themes_2"."themes_target_id" = '38'))
СГРУППИРОВАТЬ ПО base_table.vid, base_table.nid
ПРЕДЕЛ 4 СМЕЩЕНИЕ 0

И это не то, что я ожидал, так как мне нужно условие И между идентификаторами культуры и идентификаторами темы. Мне нужно что-то вроде:

И (("node__cultures"."cultures_target_id" = '5077') И (("node__themes"."themes_target_id" = '42') или ("node__themes"."themes_target_id" = '38')))

вместо

И (("node__cultures"."cultures_target_id" = '5077') или ("node__themes"."themes_target_id" = '42') или ("node__themes"."themes_target_id" = '38'))

Итак, мой вопрос: есть ли способ добиться этого с помощью And/OrConditionGroup или мне следует использовать подзапросы для сопоставления некоторых конкретных идентификаторов как для тем, так и для культур?

Рейтинг:1
флаг in

Одна маленькая вещь: вы не соответствуете своим идентификаторам и своим полям здесь:

foreach ($themes_ids как $tid) {
  $or_themes->условие('культуры', $tid); // следует использовать поле 'темы'
}
foreach ($cultures_ids как $tid) {
  $or_cultures->условие('темы', $tid); // следует использовать поле «культуры»
}

Но главная проблема здесь:

$and = $query->andConditionGroup()
  -> условие ($ or_themes);
$запрос->условие($и);
$and = $query->andConditionGroup()
  ->условие($или_культуры);
$запрос->условие($и);

Вы заключаете каждое условие ИЛИ в собственное условие И.

Но вам нужно одно условие AND, обертывающее ваши два условия OR:

$and = $query->andConditionGroup()
  -> условие ($ or_themes)
  ->условие($или_культуры);
$запрос->условие($и);

Кроме того, как упомянул @pmichelazzo, вы можете (должен? Я бы) использовать IN вместо ваших групп условий OR, например.

$cultures_and_themes = $query->andConditionGroup()
  ->условие('культуры', $cultures_ids, 'ВХОД')
  ->условие('темы', $themes_ids, 'IN');
$запрос->условие($cultures_and_themes);  
MacSim avatar
флаг um
и поскольку `andConditionGroup()` является поведением по умолчанию, мне просто нужно было связать эти условия с запросом. Я приму ваш ответ, потому что он более полный, чем ответ @pmichelazzo, но он тоже был прав. Спасибо вам, ребята
Рейтинг:1
флаг in

Используя фрагмент, который вы предоставили (и ожидаете, что он будет), вероятно, вы могли бы использовать оператор IN вместо AND:

И (("node__cultures"."cultures_target_id" = '5077') И (("node__themes"."themes_target_id" В ('42', '38'))

Таким образом, вы получаете культуры с идентификатором цели 5077 (поскольку он одинаков для обоих) и темы с идентификатором цели 42 ИЛИ 38.

Ответить или комментировать

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