Skip to main content

entityQuery simple and standard like a dynamic query

entityQuery allows developers to query Drupal entities and fields in a SQL-like way.

A Drupal site's tables and fields are never a 1-to-1 match for the site's entities and fields - entityQuery allows us to query the database as if they were 1-to-1. 

Much like you could write some SQL that returns all rows of table1 where field1 is 14, using entityQuery you can ask Drupal to return all entities of type "basic page" where the value of field_some_field is 14. 

Developers can write an entityQuery using the following method to return the QueryInterface:

$query = \Drupal::entityQuery(string $entity_type_id);

 

This first example returns all nodes of the type page where the value of field_some_field (an integer field) is 14.

$query = \Drupal::entityQuery('node')
  ->condition('type', 'page')
  ->condition('field_some_field', 14)
  ->accessCheck(TRUE);
$results = $query->execute();

 

This example shows that an entityQuery is built up by adding various conditions, sorts, ranges, and other qualifiers.

Note that the $query methods are all chainable - each one returns the $query, so we can add multiple conditions per line. 

 

For conditions, the default operator is "=", so in both of the conditions shown above, EntityQuery is looking for type=page and field_some_field=14.

If we wanted to find all basic page nodes where field_some_field > 14, then the entityQuery would look like this:

$query = \Drupal::entityQuery('node')
  ->condition('type', 'page')
  ->condition('field_some_field', 14, '>')
  ->accessCheck(TRUE);
$results = $query->execute();

 

Sorts and ranges can also be easily added:

$query = \Drupal::entityQuery('node')
  ->condition('type', 'page')
  ->condition('field_some_field', 14, '>')
  ->sort('nid', ASC)
  ->range(0, 10)
  ->accessCheck(TRUE);
$results = $query->execute();

 

Finally, we can save ourselves some typing by calling execute() directly on the $query:

$results = \Drupal::entityQuery('node')
  ->condition('type', 'page')
  ->condition('field_some_field', 14, '>')
  ->sort('nid', ASC)
  ->range(0, 10)
  ->accessCheck(TRUE)
  ->execute();

 

The execute() method returns an array of IDs for the entities found. For node entities, this is the node ID. If this was an entityQuery of users, then the ID would be user ID.

It is important to note that entityQuery does not return fully populated node objects - it only returns a list of node IDs.

Therefore, if node objects are required, a common pattern is:

$query = \Drupal::entityQuery('node')
  ->condition('type', 'page')
  ->condition('field_some_field', 14, '>')
  ->accessCheck(TRUE);
$results = $query->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($results);

 

We can create an oConditionGroup as part of the entityQuery, as well as adding additional conditions, sorts, and ranges:

$query = \Drupal::entityQuery('user');
$group = $query
  ->orConditionGroup()
  ->condition('created', '1262304000', '<')     // Jan 1, 2010
  ->condition('created', '1577836800', '>');    // Jan 1, 2020
$results = $query->condition($group)
  ->condition('status', 1)
  ->sort('created', DESC)
  ->accessCheck(TRUE)
  ->execute();

 

 

My latest entityQuery() discovery is the fact that it can be used to query field values of referenced entities. This means that if you have two node entities:

Event

  • Node ID
  • Name
  • Date
  • Location (reference field)

Location

  • Node ID
  • Name
  • Address
  • Venue type - List (text) field

Then you can use entityQuery to return all event nodes whose location's "venue type" is a particular value.

$results = \Drupal::entityQuery('node')
  ->condition('type', 'event')
  ->condition('field_location.entity:node.field_venue_type', 'boat')
  ->accessCheck(TRUE)
  ->execute();

 

you can even output the SQL from the previous use:

$query = \Drupal::entityQuery('node')
  ->condition('type', 'event')
  ->condition('field_location.entity:node.field_tag.entity:taxonomy_term', 'sailboat')
  ->accessCheck(TRUE)
  ->__toString();

Is it possible to create a condition that queries a field value of an entity that is referenced by and entity that is referenced by the entity you are querying on?

$results = \Drupal::entityQuery('node')
  ->condition('type', 'event')
  ->condition('field_location.entity:node.field_tags.entity:taxonomy_term.name', 'sailboat')
  ->accessCheck(TRUE)
  ->execute();

 

loadByProperties() method (also a method of EntityStorageInterface), which provides functionality similar to entityQuery, but returns fully-loaded node objects.

/** @var \Drupal\Core\Entity\EntityStorageInterface $node_storage */
$node_storage = $this->entityManager->getStorage('node');
$nodes = $node_storage->loadByProperties([
  'type' =>'event',
  'field_location.entity:node.field_tags.entity:taxonomy_term.name' => 'sailboat',
]);

 

It is important to understand that the entityQuery will only return entities that the user has access to - access control is performed by default.

If you want to disable access control, then add the following to the query:

$query->accessCheck(FALSE);

Starting with Drupal 10, accessCheck() will no longer be performed by default, it must be added to the query or an error will be thrown.

 

Sometimes we need to completely remove paragraph entities, for example, when you copy a site for another project.

You can use custom code in your custom module or install devel_php and run with the admin interface on /devel/php.

Remove all paragraphs on the site:

use Drupal\paragraphs\ParagraphInterface;
use Drupal\paragraphs\Entity\Paragraph;
 
$ids = \Drupal::entityQuery('paragraph')->execute();
foreach ($ids as $id) {
  $paragraph = Paragraph::load($id);
  if ($paragraph instanceof ParagraphInterface) {
    $paragraph->delete();
  }
}

 

Remove paragraphs created by an anonymous user only:

use Drupal\paragraphs\ParagraphInterface;
use Drupal\paragraphs\Entity\Paragraph;
 
$ids = \Drupal::entityQuery('paragraph')->execute();
foreach ($ids as $id) {
  $paragraph = Paragraph::load($id);
  if ($paragraph instanceof ParagraphInterface && $paragraph->getOwnerId() == 0) {
    $paragraph->delete();
  }
}

 

Remove paragraphs by type (bundle):

use Drupal\paragraphs\ParagraphInterface;
use Drupal\paragraphs\Entity\Paragraph;
 
$ids = \Drupal::entityQuery('paragraph')->execute();
foreach ($ids as $id) {
  $paragraph = Paragraph::load($id);
  if ($paragraph instanceof ParagraphInterface && $paragraph->bundle() == 'contact_us') {
    $paragraph->delete();
  }
}

 

Sometimes during development, we need to completely remove all users from the site, for example, when you copy a site for another project or work with migration.

For these purposes, we can use modules such as Entity Delete.

However sometimes more quickly just use custom code in your custom module or install devel_php and run with admin interface on /devel/php.

use Drupal\user\Entity\User;
use Drupal\user\UserInterface;
 
$ids = \Drupal::entityQuery('user')->execute();
foreach ($ids as $id) {
  $user = User::load($id);
  if ($user instanceof UserInterface && $user->id() != 0 && $user->id() != 1) {
    $user->delete();
  }
}