Как я уже упоминал, в коробке Drupal 8 есть такой полезный модуль Content Moderation. Он позволяет модерировать материалы на базе редакций (revisions): материал может быть в состоянии (moderation_state) черновик, опубликован, или в архиве. Названия состояний можно менять, можно добавлять и новые состояния, и переходы между ними. При каждой смене состояния материала создается новая редакция. Благодаря этому можно, например, оставить проверенную редакцию опубликованной для всех пользователей сайта и, в то же время, работать с более новой редакцией в состоянии черновика.
Если мы на сайте используем такую модерацию, рано или поздно нам может понадобиться программно загрузить все материалы, у которых последняя редакция в состоянии черновика. Например, чтобы их сосчитать, или еще зачем-то.
Казалось бы, нужно сделать что-то типа:
\Drupal::entityQuery('node')
->condition('moderation_state', 'draft')
->latestRevision()
->execute();
Но не тут-то было. Фильтровать entityQuery по moderation_state — нельзя, нет такого поля. Отфильтровать просто по полю status=0
(т.е. не опубликовано) тоже нельзя: во-первых у нас может быть несколько состояний модерации, в которых материал не опубликован (черновик и архив). Во-вторых, если у нас есть опубликованная редакция, то она будет дефолтной и для нее status=1
(хотя эта проблема решается с помощью latestRevision()
).
В общем, надо строить обычный запрос к БД.
Выбираем из редакций:
$query = \Drupal::database()->select('node_field_revision', 'r');
добавляем таблицу состояний модерации и node заодно:
$query->join('node', 'n', 'r.nid=n.nid');
$query->join('content_moderation_state_field_revision', 's', "r.vid=s.content_entity_revision_id AND s.content_entity_type_id='node'");
Чтобы убедиться, что это последняя редакция, добавим проверку что нет в таблице значения vid (revision id) больше данного для выбранного id материала:
$query->leftJoin('node_field_revision', 'r1', 'r.nid = r1.nid AND r1.vid > r.vid');
$query->isNull('r1.nid');
Выберем нужные поля:
$query->fields('r');
$query->fields('s');
Собственно, выберем только черновики:
$query->condition('s.moderation_state', 'draft');
Можем ограничить по типам:
$query->condition('n.type', ['article', 'product', 'another_content_type',], 'IN');
И можно запускать. В результате получится:
$query = \Drupal::database()->select('node_field_revision', 'r');
$query->join('node', 'n', 'r.nid=n.nid');
$query->join('content_moderation_state_field_revision', 's', "r.vid=s.content_entity_revision_id AND s.content_entity_type_id='node'");
$query->leftJoin('node_field_revision', 'r1', 'r.nid = r1.nid AND r1.vid > r.vid');
$query->fields('r');
$query->fields('s');
$query->isNull('r1.nid');
$query->condition('n.type', ['article', 'product', 'another_content_type',], 'IN');
$query->condition('s.moderation_state', 'draft');
$results = $query->execute()->fetchAll();
Похожий запрос строит и вью из модуля Content Moderation, показывающий табличку материалов в модерации.