##  [Продвинутая загрузка сущностей в Drupal 8](/blog/prodvinutaya-zagruzka-suschnostey-v-drupal-8) 

- [Добавить комментарий](/blog/prodvinutaya-zagruzka-suschnostey-v-drupal-8#comment-form "Поделитесь своими мыслями и мнениями.")

В [одной из прошлых статей](/blog/rabota-s-bazoi-dannyh-v-drupal-8) я рассматривал особенности построения запросов к базе данных на основе [Drupal 8 Database API](https://www.drupal.org/docs/8/api/database-api). Безусловно, с помощью данного API можно получать любые данные, содержащиеся в базе, однако, такой подход не всегда оправдан. В случае работы с сущностями правильным решением будет использование [Entity API](https://www.drupal.org/docs/8/api/entity-api), а если точнее, то сервиса **entity.query**.

### Сервис entity.query

Сервис *entity.query* представляет собой развитие [EntityFieldQuery](https://www.drupal.org/docs/7/creating-custom-modules/howtos/how-to-use-entityfieldquery-for-drupal-7) из Drupal 7 и, аналогично своему прародителю, служит для удобного получения сущностей, соответствующих определённому паттерну. Получать таким образом можно любые сущности - и контентные и конфигурационные.

Сервис предоставляет объект запроса (реализует интерфейс *\\Drupal\\Core\\Entity\\Query\\QueryInterface*) для заданного типа сущности. Существует два способа получить данный объект: правильный и н̶е̶п̶р̶а̶в̶и̶л̶ь̶н̶ы̶й̶ простой.

**Правильный способ** заключается в получении объекта с помощью контейнера сервисов. Это наиболее рекомендуемый подход. Он удобен при наличии доступа к контейнеру (например, в методе *create()* у класса плагина).

```php
/** @var \Symfony\Component\DependencyInjection\ContainerInterface $container */
$service = $container->get('entity.query');
$query = $service->get($entity_type);
```

**Простой способ** предполагает использование статического метода *entityQuery()* класса *\\Drupal*. Данный подход не рекомендуется к применению, поэтому использовать его стоит только в общем случае при отсутствии доступа к контейнеру сервисов.

```php
$query = \Drupal::entityQuery($entity_type);
```

В обоих случаях в переменную *$entity\_type* нужно передать идентификатор типа сущностей, с которыми требуется работать (например, node, user, comment и т.д.).

### Загружаем сущности правильно

Перейдём к конкретным примерам. Для простоты будем создавать объект запроса с помощью статического метода класса *\\Drupal*.

**Пример:** *Получить идентификаторы всех опубликованных нод типа article.*

```php
$query = \Drupal::entityQuery('node');
$query->condition('type', 'article');
$query->condition('status', 1);
$nids = $query->execute();
```

Здесь используются методы *condition()* и *execute()*. Первый используется для применения условий к запросу, а второй непосредственно для выполнения запроса и получения идентификаторов загружаемых сущностей. В метод *condition()* можно передавать четыре параметра:

- *field* - машинное имя поля сущности, для которого определяется условие (является обязательным параметром);
- *value* - значение поля, может быть скалярным значением или массивом. Если параметр опущен, то будет использоваться *NULL*;
- *operator* - оператор условия. Если параметр опущен, то ему будет присвоено значение «=»;
- *langcode* - код языка. Если параметр опущен, любой перевод будет удовлетворять условию. В противном случае выборка будет ограничена заданным переводом.

В *entity.query* не существует различий между свойствами и полями сущности, как это было при использовании *EntityFieldQuery* в Drupal 7. Теперь определение условий для свойств и полей делается в рамках метода *condition()*.

Параметр *field* должен содержать имя поля сущности, за которым может следовать имя столбца поля. В свою очередь, столбец может быть ссылочным свойством (в случае ссылочного поля), за которым может следовать имя поля целевой сущности и т. д. Кроме того, тип целевой сущности можно указать, добавив «:target\_entity\_type\_id» после ссылочного свойства.

В качестве оператора допустимы следующие значения:

- =, &lt;&gt;, &gt;, &gt;=, &lt;, &lt;=, STARTS\_WITH, CONTAINS, ENDS\_WITH: Эти операторы предполагают, что тип *value* соответствует типу значений поля;
- IN, NOT IN: Эти операторы предполагают, что *value* является массивом и тип каждого элемента соответствует типу значений поля;
- BETWEEN: Этот оператор предполагает, что *value* является массивом из двух элементов, тип которых соответствует типу значений поля;

**Пример:** *Получить идентификаторы всех опубликованных нод типа poem или fairy\_tale, которые в тизере поля body содержат фразу «У лукоморья дуб зеленый», помечены трёмя и более тегами, один из которых «Сказки Пушкина», и опубликованы пользователем с идентификатором 1799 в течение последней недели.*

```php
$query = \Drupal::entityQuery('node');
$query->condition('type', ['poem', 'fairy_tale'], 'IN');
$query->condition('body.summary', 'У лукоморья дуб зеленый', 'CONTAINS');
$query->condition('field_tags.%delta', 2);
$query->condition('field_tags.entity:taxonomy_term.name', 'Сказки Пушкина');
$query->condition('uid', 1799);
$query->condition('created', time() - 604800, '>');
$query->condition('status', 1);
$nids = $query->execute();
```

Стоит отметить, что такие значения параметра *field*, как *body* и *body.value* будут идентичны, так как *value* является столбцом по умолчанию. В случае multiple-поля можно определять условие относительно конкретного значения с помощью указания дельты. Например, параметр *field\_tags.7* будет указывать на восьмое значение поля *field\_tags*. Кроме того, можно задать условие для самой дельты, используя «%delta», как это было реализовано в примере выше.

Тип целевой сущности указывать необязательно, если он точно известен. Например, если известно, что поле *field\_tags* хранит ссылки на термины таксономии, то параметры *field\_tags.entity.name* и *field\_tags.entity:taxonomy\_term.name* будут тождественны.

**Пример:** *Получить идентификаторы всех нод типа article, которые имеют тизер поля body и не отмечены тегами.*

```php
$query = \Drupal::entityQuery('node');
$query->condition('type', 'article');
$query->exists('body.summary');
$query->notExists('field_tags');
$nids = $query->execute();
```

За проверку поля на пустоту отвечают методы *exists()* и *notExists()*. Важно обратить внимание, что эти методы не выполняют проверку на наличие поля у сущности. Это значит, что при проверке несуществующего поля будет получена ошибка. При этом вызов *exists($field)* идентичен вызову *condition($field, NULL, 'IS NOT NULL')*, а вызов *notExists($field)* идентичен *condition($field, NULL, 'IS NULL')*.

**Пример:** *Получить идентификаторы всех нод, упорядоченные по дате создания.*

```php
$query = \Drupal::entityQuery('node')->sort('created', 'DESC');
$nids = $query->execute();
```

**Пример:** *Получить количество всех нод.*

```php
$query = \Drupal::entityQuery('node')->count();
$count = $query->execute();
```

**Пример:** *Получить идентификаторы последних 10 нод.*

```php
$query = \Drupal::entityQuery('node');
$query->sort('created', 'DESC');
$query->range(0, 10);
$nids = $query->execute();
```

С помощью метода *pager()* также есть возможность управления результатами выборки на основе пагинации.

**Пример:** *Получить идентификаторы всех нод, опубликованных пользователем с идентификатором 1 или с именем «superduperuser».*

```php
$query = \Drupal::entityQuery('node', 'OR');
$query->condition('uid', 1);
$query->condition('uid.entity.name', 'superduperuser');
$nids = $query->execute();
```

Посредством значения параметра *conjunction* в методе создания объекта запроса можно управлять объединением условий. Допустимы значения *AND* и *OR*. В первом случае условия будут соединены конъюнкцией, а во втором - дизъюнкцией. По умолчанию условия объединяются с помощью конъюнкции.

**Пример:** *Получить идентификаторы всех нод типа article, опубликованных пользователем с идентификатором 1 или с именем «superduperuser».*

```php
$query = \Drupal::entityQuery('node');
$query->condition('type', 'article');

$group = $query->orConditionGroup();
$group->condition('uid', 1);
$group->condition('uid.entity.name', 'superduperuser');

$query->condition($group);
$nids = $query->execute();
```

Методы *andConditionGroup()* и or*ConditionGroup()* позволяют добавлять условия, связанные конъюнкцией и дизъюнкцией, независимо от параметра *conjunction*. Эти методы позволяют реализовывать действительно гибкие условия.

**Пример:** *Получить объекты нод, идентификаторы которых были получены с помощью entity.query.*

```php
$nids = \Drupal::entityQuery('node')->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
```

Метод *execute()* возвращает массив (при использовании метода *count()* будет возвращено число), значения которого всегда будут содержать идентификаторы сущностей. Ключи массива могут быть как идентификаторами ревизий сущностей, так и идентификаторами самих сущностей в зависимости от наличия поддержки ревизий у заданного типа сущностей.

Методы *currentRevision()*, *latestRevision()* и *allRevisions()* позволяют управлять идентификаторами ревизий в результирующем массиве. При использовании первого метода ключами массива будут идентификаторы текущих ревизий, а при использовании второго метода - идентификаторы самых поздних. Наконец, при использовании третьего метода ключами массива будут идентификаторы всех ревизий.

### Полезные материалы

- [Работа с базой данных в Drupal 8](/blog/rabota-s-bazoi-dannyh-v-drupal-8)
- [The Drupal 8 version of EntityFieldQuery](https://www.sitepoint.com/drupal-8-version-entityfieldquery)
- [Entity queries and loading entities](https://docs.acquia.com/article/lesson-61-entity-queries-and-loading-entities)
- [Query on Drupal 8 with EntityQuery](http://drupalsun.com/zhilevan/2017/01/14/query-drupal-8-entityquery)



[Drupal](/blog/drupal)

[Drupal 8](/blog/drupal-8)

 

Ещё по теме

[Сущности Drupal 8](/blog/suschnosti-drupal-8)

 

 

   

 [](https://drupal-coder.ru/rss.xml) 

 

 ## Комментарии

Алексей Зубенко

четверг, апреля 26, 2018 - 11:57

Ключи, указываемые в "condition", у разных типов сущностей отличаются. Но можно унифицировать свой запрос, предварительно получая список ключей, примерно так:

`$keys = \Drupal::entityTypeManager()->getDefinition('node')->getKeys();`

- [Ответить](/comment/reply/node/147/comment/19)



Alexey Murz Korepov

вторник, апреля 16, 2019 - 09:04

А можно каким-то образом в результате entityQuery сразу получить готовую загруженную сущность без промежуточного массива?  
Т.е. вместо  
`<br></br>$query = \Drupal::entityQuery('node', 'OR');<br></br>$query->condition('uid', 1);<br></br>$query->condition('uid.entity.name', 'superduperuser');<br></br>$query->range(0, 1);<br></br>$nids = $query->execute();<br></br>$node = Node::load(current($nids));<br></br>`  
иметь что-то вида:  
`<br></br>$node = $query->getFirstAndLoad();<br></br>`



- [Ответить](/comment/reply/node/147/comment/161)



Андрей Тымчук

пятница, апреля 19, 2019 - 08:30

Такой возможности нет.

- [Ответить](/comment/reply/node/147/comment/162)





Антон Банников

среда, сентября 2, 2020 - 07:42

Я долго изучал, как работает правильный метод вызова запросов для сущностей. В итоге нашел статью, что он уже устарел. <https://www.drupal.org/node/2849874>



- [Ответить](/comment/reply/node/147/comment/713)



Андрей Тымчук

пятница, сентября 25, 2020 - 08:12

Да, сервис `entity.query` объявлен устаревшим и в Drupal 9 его уже нет. Теперь объект запроса нужно получать через хранилище конкретного типа сущностей. При этом метод `\Drupal::entityQuery()` всё ещё доступен для не объектно-ориентированного кода.



- [Ответить](/comment/reply/node/147/comment/810)





Гость

понедельник, сентября 6, 2021 - 12:06

как подписаться на RSS?



- [Ответить](/comment/reply/node/147/comment/1727)



Алексей Синица

понедельник, сентября 6, 2021 - 13:03

https://drupal-coder.ru/rss.xml 

- [Ответить](/comment/reply/node/147/comment/1728)





## Добавить комментарий

  Вы должны включить JavaScript чтобы использовать эту форму.

 Ваше имя  

 Комментарий 

 

[О текстовых форматах](/filter/tips "Opens in new window")



 

 

 Отправить