Drupal 9: Loading Configuration Entities Using Entity Query

Whilst working on a module I found that I needed to do something that was difficult to find information for. I think part of that is because the solution is simpler than I was thinking, but it did cause me to get lost in source code and internet issues for a while.

What I needed to do was to load in a list of configuration entities that matched certain parameters. The configuration entity I was dealing with was used to manipulate the output of a form, but only if certain conditions were met first. I thought that this information might be useful to others who are looking to load configuration entities.

In this article I will show how to search for configuration entities and how to load those entities once found. I'll be using blocks for the examples here, but the same rules will apply to any configuration entities you want to load.

Loading The Entity Query Service

What we need to do first is get the entity query service. In case it wasn't obvious (and it wasn't to me) you can search for configuration entities in the same way as you would any other sort of entity. If you have used entity query to find users or pages of content then this is the same mechanism. There are, however, a couple of ways to go about doing this.

The first (and simplest) way of loading entity query is to just grab the object for the configuration entity we want to load.

$entityQuery = \Drupal::entityQuery('block');

Whilst using this method does allow us to search for configuration entities, it doesn't allow us to load those entities. To do that we need to use entity_type.manager service to find the storage for the configuration entity we want to load.

I have found that a slightly better approach to this is to use the entity_type.manager service from the start. We can use this service to load the entity storage interface we need, which then gives us access to the entity query instance itself.

$entityStorage = \Drupal::service('entity_type.manager')
  ->getStorage('block');

The $entityStorage variable now contains an instance of the class \Drupal\Core\Config\Entity\ConfigEntityStorage, which is a wrapper around all configuration entities in a Drupal site. Using this class we can get access to the entity query instance using the getQuery() method.

$entityQuery = $entityStorage->getQuery();

We can now perform searches on configuration entities.

Searching For Configuration Entities Using Entity Query

Now that we have the entity query instance we can use it to search for our configuration entities.

The simplest thing we can do is just load a list of all of the configuration entities available on the site. After running the below code we will have an array containing the names of all the entities we are looking for.

$configList = $entityQuery
  ->execute();

This would produce the something like the following list for the block configuration entities available. 

Array
(
    [breadcrumbs] => breadcrumbs
    [claro_breadcrumbs] => claro_breadcrumbs
    [claro_content] => claro_content
    [claro_local_actions] => claro_local_actions
    [claro_messages] => claro_messages
    [claro_page_title] => claro_page_title
    [claro_primary_local_tasks] => claro_primary_local_tasks
    [claro_secondary_local_tasks] => claro_secondary_local_tasks
    [seven_local_actions] => seven_local_actions
    [seven_local_tasks] => seven_local_tasks
    [seven_login] => seven_login
    [seven_messages] => seven_messages
    [seven_page_title] => seven_page_title
)

Note that in contrast to searching for other sorts of entities the configuration entities will be identified by their machine name. If we searched for taxonomy terms then the taxonomy term ID would be the identifying item in the above list.

It is also possible to search for configuration entities using the fields for that entity. For example, the help block in a newly installed site would look something like this.

uuid: 44b4b560-ee33-40c7-bfe6-bdceaff1390f
langcode: en
status: true
dependencies:
  module:
    - system
  theme:
    - claro
id: breadcrumbs
theme: claro
region: breadcrumb
weight: -7
provider: null
plugin: system_breadcrumb_block
settings:
  id: system_breadcrumb_block
  label: Breadcrumbs
  label_display: '0'
  provider: system
visibility: {  }

Each of these fields can be searched for using the entity query instance. If we wanted to find all active blocks on the claro theme then we would add two conditions for 'status' and 'theme'.

$configList = $entityQuery
  ->condition('status', 'true')
  ->condition('theme', 'claro')
  ->execute();

It is also possible to "drill into" the nested configuration items in the conditions of the query. The following example will find all blocks that have the "label" of "Breadcrumb". As this is stored within the "settings" section of the block configuration we need to inform entity query of this by using a "." to indicate the nested nature of the field.

$configList = $entityQuery
  ->condition('settings.label', 'Breadcrumbs')
  ->execute();

We can even search for blocks using matching queries. The following query will find all blocks that contain the word "breadcrumb" in their id.

$configList = $entityQuery
  ->condition('id', 'breadcrumb', 'CONTAINS')
  ->execute();

As well as CONTAINS you can also use STARTS_WITH or ENDS_WITH to perform searches within fields.

Now that we have our list of entities we need to load them.

Loading Configuration Entities With The Results Of Entity Query

Loading configuration entities is done just like loading any other entity.

If you used the "\Drupal::entityQuery('block');" code to load the entity query instance then you will need to load in the entity_type.manager service in order to load the correct storage type and then perform a load operation.

The loadMultiple() method on the entity storage object accepts a list of the entities you want to load. This returns a list of loaded entity objects.

$entityStorage = \Drupal::entityTypeManager()->getStorage('block');
$blocks = $entityStorage->loadMultiple($configList);

If you used the entity_type.manager service to load the entity query object then you will already have access to this object. In which case you just need to load the entities.

$configEntities = $entityStorage->loadMultiple($configList);

As a side note, if you just want to load all the configuration entities of a particular type then you don't need to use entity query at all. You can just call loadMultiple() without passing any parameters to the method to load all the configuration entities available.

$configEntities = $entityStorage->loadMultiple();

It is possible to alter configuration entities as they are retrieved from the database by adding lines of code to the settings.php file of the site. This will cause any configuration entity you load to be altered on the fly. If you don't want these alterations to be added to the configuration entity then you can use the loadMultipleOverrideFree() method to load the entities directly from the database.

$configEntities = $entityStorage->loadMultipleOverrideFree($configList);

This will bypass the alterations made to the configuration entities in the settings.php file.

After using the above methods you will have an array of configuration entity objects.

As it can be a little memory intensive to load lots of entity objects at the same time you can also load them one at a time using the load() or loadOverrideFree() methods to load one configuration entity at a time.

foreach ($configList as $config) {
  $configEntity = $entityStorage->load($config);
  // Do something with the entity.
}

Once you have loaded your configuration entities you can do whatever you need to do.

For my use case it was using different configuration entities to change how different form were rendered, depending on how the user had set up the configuration. I used the entity query to find the configuration entities relevant to the form being rendered and then loaded them to pull out the form alterations required.

Using these load methods doesn't mean you have immutable configuration entity though. The configuration entities, once loaded, can also be changed and saved back to the database.

Loading By Properties

Instead of using the entity query directly it is possible to use the loadByProperties() method of the entity storage object. This essentially calls getQuery() method and then proceeds to use the entity query to search for the configuration entities that match the passed properties.

$configEntities = \Drupal::service('entity_type.manager')
  ->getStorage('block')
  ->loadByProperties(['theme' => 'claro']);

The loadByProperties() method will also take care of loading the configuration entities, and so what we get back from the method is a fully loaded array of configuration entities.

We can also use the same method to search for nested configuration items.

$configEntities = \Drupal::service('entity_type.manager')
  ->getStorage('block')
  ->loadByProperties(['settings.id' => 'system_breadcrumb_block']);

This is a useful shortcut if you don't want to do anything special with the entity query object itself. One limitation of this approach is that we can't use CONTAINS conditions to search for configuration entities.

Examples

Due to the fact that loading entities and loading configuration entities are essentially the same it is a little difficult to find examples of this in action.

Views

The Views system in Drupal uses the entity query and entity_type.manager services separately to load views that meet different criteria. This block of code can be found in the Views class that allows all enabled Views to be loaded using a single line of code.

  public static function getEnabledViews() {
    $query = \Drupal::entityQuery('view')
      ->condition('status', TRUE)
      ->execute();

    return \Drupal::entityTypeManager()->getStorage('view')->loadMultiple($query);
  }

Blocks

The blocks module makes use of the loadByProperties() method a few times, most notably when a new theme is installed or when an existing theme is rebuilt. The block configuration entity query is used to find and populate the theme with blocks that have been assigned to particular regions.

$blocks = \Drupal::entityTypeManager()->getStorage('block')->loadByProperties(['theme' => $theme]);

Webform

The Webform module uses configuration entities quite a lot. In fact, every Webform is a configuration entity itself and so can be searched for and loaded using the methods shown above.

A good example of this in use is the Webform search autocomplete method, found in the class \Drupal\webform\Controller\WebformEntityController. This method takes a number of parameters and uses these to query the database for matching configuration entities. It then loads the found entities and returns a JSON response.

public function autocomplete(Request $request, $templates = FALSE, $archived = FALSE) {
    $q = $request->query->get('q');

    $webform_storage = $this->entityTypeManager()->getStorage('webform');

    $query = $webform_storage->getQuery()
      ->range(0, 10)
      ->sort('title');
    // Query title and id.
    $or = $query->orConditionGroup()
      ->condition('id', $q, 'CONTAINS')
      ->condition('title', $q, 'CONTAINS');
    $query->condition($or);

    // Code removed for brevity.

    $entity_ids = $query->execute();

    if (empty($entity_ids)) {
      return new JsonResponse([]);
    }
    $webforms = $webform_storage->loadMultiple($entity_ids);

    $matches = [];
    foreach ($webforms as $webform) {
      if ($webform->access('view')) {
        $value = new FormattableMarkup('@label (@id)', ['@label' => $webform->label(), '@id' => $webform->id()]);
        $matches[] = ['value' => $value, 'label' => $value];
      }
    }

    return new JsonResponse($matches);
  }

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
3 + 16 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.