Entities: loading and fields
Nodes, users, taxonomy terms, files — all of these are entities. They share one way of loading, reading fields and saving. Once you learn it on nodes, you can work with everything else. No separate files are needed for this: the code is written where the data is needed — in a controller, a form, a block or a hook.
Storage
Every operation goes through the storage of the entity type. In a controller you get it like this:
// web/modules/custom/blog/src/Controller/BlogController.php
public function first(): array
{
$storage = $this->entityTypeManager()->getStorage('node');
$node = $storage->load(1);
return ['#markup' => $node->label()];
}getStorage('node') returns the node storage, load(1) the node with number 1, label() its title. If you tie the method to a route, the page shows the title of the first article. When there is no node with that number, load() returns NULL.
In hooks and services the storage is taken through the service: \Drupal::entityTypeManager()->getStorage('node').
Several entities
// web/modules/custom/blog/src/Controller/BlogController.php
$nodes = $storage->loadMultiple([1, 2, 3]);
$articles = $storage->loadByProperties(['type' => 'article', 'status' => 1]);Both methods return an array keyed by entity number. loadByProperties() searches by exact values: here — all published articles.
Common methods
| Method | What it returns |
|---|---|
id() | The number |
label() | The title |
bundle() | The variety: for a node — the type, article |
getEntityTypeId() | The entity type: node |
toUrl()->toString() | The page address |
isPublished() | Whether the node is published |
getCreatedTime() | The creation time in seconds |
getOwner() | The author — a user entity |
Reading fields
// web/modules/custom/blog/src/Controller/BlogController.php
$body = $node->get('body')->value;
$summary = $node->get('body')->summary;get('field_name') returns a field object, and the value itself is in its property. For simple fields — text, numbers, dates — it is value. Field names are machine names: body, field_image, field_tags.
Before reading a field a content type may not have, check: $node->hasField('field_image').
Reference fields
Tags and the image are references to other entities:
// web/modules/custom/blog/src/Controller/BlogController.php
$imageId = $node->get('field_image')->target_id;
$image = $node->get('field_image')->entity;
$imageUrl = $image?->createFileUrl();target_id is the number of the referenced entity, entity is the entity itself, already loaded. If no image is set, entity is NULL.
Several values
The tags field holds several values. They are read in a loop:
// web/modules/custom/blog/src/Controller/BlogController.php
$names = [];
foreach ($node->get('field_tags')->referencedEntities() as $term) {
$names[] = $term->label();
}$names gets the names of all the tags of the article. Writing ->value or ->entity without a loop returns only the first value.
Changing
// web/modules/custom/blog/src/Controller/BlogController.php
$node->set('title', 'New title');
$node->set('body', ['value' => 'New text.', 'format' => 'basic_html']);
$node->save();set() changes the value in the object, and it reaches the database only after save(). A text field has two properties: the text itself and the format used to process it on output.
Other entities
// web/modules/custom/blog/src/Controller/BlogController.php
$user = $this->entityTypeManager()->getStorage('user')->load(1);
$email = $user->getEmail();
$terms = $this->entityTypeManager()
->getStorage('taxonomy_term')
->loadByProperties(['vid' => 'tags']);Only the entity type name changes. vid is the taxonomy vocabulary.
Common mistake
load() returns NULL when the entity does not exist, and calling $node->label() then fails with "Call to a member function on null". Check the result before using it. And if the number comes from the address, let the route load the node — the {node} parameter from the lesson about parameters returns 404 for a missing number by itself.