Запросы к сущностям
Запрос к сущностям (entity query) ищет материалы по условиям: тип, статус, значение поля. Он возвращает только номера найденных сущностей, а сами сущности затем загружает хранилище. С его помощью страница /blog наконец покажет настоящие статьи.
Простой запрос
Запрос создаёт хранилище из прошлого урока:
// web/modules/custom/blog/src/Controller/BlogController.php
$storage = $this->entityTypeManager()->getStorage('node');
$ids = $storage->getQuery()
->accessCheck(TRUE)
->condition('type', 'article')
->execute();
$nodes = $storage->loadMultiple($ids);condition('type', 'article') оставляет только статьи, execute() выполняет запрос и возвращает массив номеров. Вызов accessCheck() обязателен: TRUE означает «только то, что разрешено видеть текущему пользователю».
Условия, сортировка, ограничение
// web/modules/custom/blog/src/Controller/BlogController.php
$ids = $storage->getQuery()
->accessCheck(TRUE)
->condition('type', 'article')
->condition('status', 1)
->condition('created', strtotime('-30 days'), '>=')
->sort('created', 'DESC')
->range(0, 10)
->execute();Запрос вернёт не больше десяти опубликованных статей за последние 30 дней, сначала новые. Третий аргумент condition() — оператор сравнения; без него проверяется равенство. range(0, 10) — смещение и количество.
| Запись | Что означает |
|---|---|
condition('status', 1) | Равно |
condition('created', $time, '>=') | Сравнение: >, >=, <, <=, <> |
condition('title', '%drupal%', 'LIKE') | Содержит подстроку |
condition('type', ['article', 'page'], 'IN') | Одно из значений |
condition('field_tags.entity.name', 'PHP') | По полю связанной сущности |
exists('field_image') | Поле заполнено |
count() | Вернуть количество вместо номеров |
Список статей на странице
Соберём всё в методе list():
// web/modules/custom/blog/src/Controller/BlogController.php
public function list(): array
{
$storage = $this->entityTypeManager()->getStorage('node');
$ids = $storage->getQuery()
->accessCheck(TRUE)
->condition('type', 'article')
->condition('status', 1)
->sort('created', 'DESC')
->range(0, $this->config('blog.settings')->get('items_per_page'))
->execute();
$nodes = $storage->loadMultiple($ids);
return $this->entityTypeManager()
->getViewBuilder('node')
->viewMultiple($nodes, 'teaser');
}Построитель вывода getViewBuilder('node') превращает материалы в массив вывода. Второй аргумент — режим отображения: teaser — краткий вид, full — полный. Страница /blog покажет свежие статьи так, как настроено на вкладке «Управление отображением» типа материала, и шаблоном node--article.html.twig из темы.
Создание
// в любом месте, например в команде Drush
$node = \Drupal::entityTypeManager()->getStorage('node')->create([
'type' => 'article',
'title' => 'Новая статья',
'body' => ['value' => 'Текст статьи.', 'format' => 'basic_html'],
'uid' => 1,
'status' => 1,
]);
$node->save();create() только создаёт объект, в базу его записывает save(). Обязательны type и title; uid — номер автора, status — опубликован ли материал. После сохранения $node->id() вернёт присвоенный номер.
Удаление
// в любом месте, например в команде Drush
$node->delete();
$storage->delete($storage->loadMultiple($ids));Первая строка удаляет один материал, вторая — сразу несколько. Удаление необратимо.
Частая ошибка
Запрос без accessCheck() завершается исключением: Drupal требует явно решить, учитывать ли права. accessCheck(TRUE) нужен для всего, что выводится посетителю. FALSE допустимо только в служебных задачах — cron, команды Drush, обновления, — где пользователя нет. Перепутав, легко показать гостю неопубликованные материалы.