Building queries

Now that we have an entity query factory on our hands, we can build a query that is made up of conditions and all sorts of typical query elements. Here's a simple example of querying for the last 10 published article nodes:

$query
->condition('type', 'article')
->condition('status', TRUE)
->range(0, 10)
->sort('created', 'DESC');
$ids = $query->execute();

The first thing you can see is that the methods on the factory are chainable. We have some expected methods to set conditions, range, sorting, and so on. As you can already deduce, the first parameter is the field name and the second is the value. An optional third parameter can also be the operator for the condition.

I strongly recommend you check out the \Drupal\Core\Entity\Query\QueryInterface class for some documentation about these methods, especially the condition() method which is the most complex.

Here is a slightly more complex condition that would return nodes of two different types:

->condition('type', ['article', 'page'], 'IN') 

Additionally, you can also use condition groups, with OR or AND conjunctions:

$query
->condition('status', TRUE);
$or = $query->orConditionGroup()
->condition('title', 'Drupal', 'CONTAINS')
->condition('field_tags.entity.name', 'Drupal', 'CONTAINS');
$query->condition($or);
$ids = $query->execute();

In the previous query, we see a few new things. First, we create a condition group of the type OR in which we add two conditions. One of them checks whether the node title field contains the string "Drupal". The other checks whether any of the entities referenced by the field_tags field (in this case taxonomy terms) has the string "Drupal" in their name. So, you can see the power we have in traversing into referenced entities. Finally, we use this condition group as the first parameter to the condition() method of the query (instead of field name and value).

Entity queries for the Node entity type take access restrictions into account as they are run from the context of the current user. This means that, for example, a query for unpublished nodes triggered on a page hit by an anonymous user is not going to return results, but it will if triggered by an administrator. You can disable this by adding the ->accessCheck(FALSE) instruction to the query IF you are sure the results are not going to expose unwanted content to users. We will talk more about node access in a later chapter.

Configuration entities work in the same way. We get the query factory for that entity type and build a query. Under the hood, the query is of course run differently due to the flat nature of the storage.

Each configuration entity gets one record in the database, so they need to be loaded and then examined. Moreover, the conditions can be written to also match the nested nature of configuration entity field data. For example:

$query = \Drupal::entityTypeManager()->getStorage('view')->getQuery();
$query
->condition('display.*.display_plugin', 'page');
$ids = $query->execute();

This query searches for all the View configuration entities that have the display plugin of the type "page". The condition essentially looks inside the display array for any of the elements (hence the * wildcard). If any of these elements has a display_plugin key with the value "page", it's a match. This is what an example view entity looks like in YAML format:

...
base_field: nid
core: 8.x
display:
default:
display_options:
...
display_plugin: default
display_title: Master
...
page_1:
display_options:
...
display_plugin: page
display_title: Page

I removed a bunch of data from this entity just to keep it short. But as you can see, we have the display array, with the default and page_1 elements, and each has a display_plugin key with a plugin ID.