Entity CRUD, editing, and view hooks

Same name in other branches
  1. 8.9.x core/lib/Drupal/Core/Entity/entity.api.php \entity_crud
  2. 10 core/lib/Drupal/Core/Entity/entity.api.php \entity_crud
  3. 11.x core/lib/Drupal/Core/Entity/entity.api.php \entity_crud

Hooks used in various entity operations.

Entity create, read, update, and delete (CRUD) operations are performed by entity storage classes; see the Entity API topic for more information. Most entities use or extend the default classes: \Drupal\Core\Entity\Sql\SqlContentEntityStorage for content entities, and \Drupal\Core\Config\Entity\ConfigEntityStorage for configuration entities. For these entities, there is a set of hooks that is invoked for each CRUD operation, which module developers can implement to affect these operations; these hooks are actually invoked from methods on \Drupal\Core\Entity\EntityStorageBase.

For content entities, viewing and rendering are handled by a view builder class; see the Entity API topic for more information. Most view builders extend or use the default class \Drupal\Core\Entity\EntityViewBuilder.

Entity editing (including adding new entities) is handled by entity form classes; see the Entity API topic for more information. Most entity editing forms extend base classes \Drupal\Core\Entity\EntityForm or \Drupal\Core\Entity\ContentEntityForm. Note that many other operations, such as confirming deletion of entities, also use entity form classes.

This topic lists all of the entity CRUD and view operations, and the hooks and other operations that are invoked (in order) for each operation. Some notes:

  • Whenever an entity hook is invoked, there is both a type-specific entity hook, and a generic entity hook. For instance, during a create operation on a node, first hook_node_create() and then hook_entity_create() would be invoked.
  • The entity-type-specific hooks are represented in the list below as hook_ENTITY_TYPE_... (hook_ENTITY_TYPE_create() in this example). To implement one of these hooks for an entity whose machine name is "foo", define a function called mymodule_foo_create(), for instance. Also note that the entity or array of entities that are passed into a specific-type hook are of the specific entity class, not the generic Entity class, so in your implementation, you can make the $entity argument something like $node and give it a specific type hint (which should normally be to the specific interface, such as \Drupal\node\NodeInterface for nodes).
  • $storage in the code examples is assumed to be an entity storage class. See the Entity API topic for information on how to instantiate the correct storage class for an entity type.
  • $view_builder in the code examples is assumed to be an entity view builder class. See the Entity API topic for information on how to instantiate the correct view builder class for an entity type.
  • During many operations, static methods are called on the entity class, which implements \Drupal\Core\Entity\EntityInterface.

Entities, revisions and translations

A content entity can have multiple stored variants: based on its definition, it can be revisionable, translatable, or both.

A revisionable entity can keep track of the changes that affect its data. In fact all previous revisions of the entity can be stored and made available as "historical" information. The "default" revision is the canonical variant of the entity, the one that is loaded when no specific revision is requested. Only changes to the default revision may be performed without triggering the creation of a new revision, in any other case revision data is not supposed to change. Aside from historical revisions, there can be "pending" revisions, that contain changes that did not make their way into the default revision. Typically these revisions contain data that is waiting for some form of approval, before being accepted as canonical.

A translatable entity can contain multiple translations of the same content. Content entity data is stored via fields, and each field can have one version for each enabled language. Some fields may be defined as untranslatable, which means that their values are shared among all translations. The "default" translation is the canonical variant of the entity, the one whose content will be accessible in the entity field data. Other translations can be instantiated from the default one. Every translation has an "active language" that is used to determine which field translation values should be handled. Typically the default translation's active language is the language of the content that was originally entered and served as source for the other translations.

An entity that is both revisionable and translatable has all the features described above: every revision can contain one or more translations. The canonical variant of the entity is the default translation of the default revision. Any revision will be initially loaded as the default translation, the other revision translations can be instantiated from this one. If a translation has changes in a certain revision, the translation is considered "affected" by that revision, and will be flagged as such via the "revision_translation_affected" field. With the built-in UI, every time a new revision is saved, the changes for the edited translations will be stored, while all field values for the other translations will be copied as-is. However, if multiple translations of the default revision are being subsequently modified without creating a new revision when saving, they will all be affected by the default revision. Additionally, all revision translations will be affected when saving a revision containing changes for untranslatable fields. On the other hand, pending revisions are not supposed to contain multiple affected translations, even when they are being manipulated via the API.

Entity characteristics

In addition to entity interfaces for revisionable and translatable interfaces, there are interfaces for other kinds of entity functionality.

Create operations

To create an entity:

$entity = $storage->create();
// Add code here to set properties on the entity.
// Until you call save(), the entity is just in memory.
$entity->save();

There is also a shortcut method on entity classes, which creates an entity with an array of provided property values: \Drupal\Core\Entity::create().

Hooks invoked during the create operation:

See Save operations below for the save portion of the operation.

Read/Load operations

To load (read) a single entity:

$entity = $storage->load($id);

To load multiple entities:

$entities = $storage->loadMultiple($ids);

Since load() calls loadMultiple(), these are really the same operation. Here is the order of hooks and other operations that take place during entity loading:

When an entity is loaded, normally the default entity revision is loaded. It is also possible to load a different revision, for entities that support revisions, with this code:

$entity = $storage->loadRevision($revision_id);

This involves the same hooks and operations as regular entity loading.

The "latest revision" of an entity is the most recently created one, regardless of it being default or pending. If the entity is translatable, revision translations are not taken into account either. In other words, any time a new revision is created, that becomes the latest revision for the entity overall, regardless of the affected translations. To load the latest revision of an entity:

$revision_id = $storage->getLatestRevisionId($entity_id);
$entity = $storage->loadRevision($revision_id);

As usual, if the entity is translatable, this code instantiates into $entity the default translation of the revision, even if the latest revision contains only changes to a different translation:

$is_default = $entity->isDefaultTranslation();
// returns TRUE

The "latest translation-affected revision" is the most recently created one that affects the specified translation. For example, when a new revision introducing some changes to an English translation is saved, that becomes the new "latest revision". However, if an existing Italian translation was not affected by those changes, then the "latest translation-affected revision" for Italian remains what it was. To load the Italian translation at its latest translation-affected revision:

$revision_id = $storage->getLatestTranslationAffectedRevisionId($entity_id, 'it');
$it_translation = $storage->loadRevision($revision_id)
    ->getTranslation('it');

Save operations

To update an existing entity, you will need to load it, change properties, and then save; as described above, when creating a new entity, you will also need to save it. Here is the order of hooks and other events that happen during an entity save:

Some specific entity types invoke hooks during preSave() or postSave() operations. Examples:

Note that all translations available for the entity are stored during a save operation. When saving a new revision, a copy of every translation is stored, regardless of it being affected by the revision.

Editing operations

When an entity's add/edit form is used to add or edit an entity, there are several hooks that are invoked:

Delete operations

To delete one or more entities, load them and then delete them:

$entities = $storage->loadMultiple($ids);
$storage->delete($entities);

During the delete operation, the following hooks and other events happen:

Some specific entity types invoke hooks during the delete process. Examples:

Individual revisions of an entity can also be deleted:

$storage->deleteRevision($revision_id);

This operation invokes the following operations and hooks:

View/render operations

To make a render array for a loaded entity:

// You can omit the language ID if the default language is being used.
$build = $view_builder->view($entity, 'view_mode_name', $language->getId());

You can also use the viewMultiple() method to view multiple entities.

Hooks invoked during the operation of building a render array:

View builders for some types override these hooks, notably:

During the rendering operation, the default entity viewer runs the following hooks and operations in the pre-render step:

Some specific builders have specific hooks:

After this point in rendering, the theme system takes over. See the Theme system and render API topic for more information.

Other entity hooks

Some types of entities invoke hooks for specific operations:

See also

\Drupal\Core\Entity\RevisionableInterface

\Drupal\Core\Entity\RevisionableStorageInterface

\Drupal\Core\Entity\TranslatableInterface

\Drupal\Core\Entity\TranslatableStorageInterface

\Drupal\Core\Entity\TranslatableRevisionableInterface

\Drupal\Core\Entity\TranslatableRevisionableStorageInterface

entity_characteristics

File

core/lib/Drupal/Core/Entity/entity.api.php, line 19

Functions

Title Sort descending File name Summary
hook_entity_build_defaults_alter core/lib/Drupal/Core/Entity/entity.api.php Alter entity renderable values before cache checking during rendering.
hook_entity_bundle_delete core/lib/Drupal/Core/Entity/entity.api.php Act on entity_bundle_delete().
hook_entity_create core/lib/Drupal/Core/Entity/entity.api.php Acts when creating a new entity.
hook_entity_delete core/lib/Drupal/Core/Entity/entity.api.php Respond to entity deletion.
hook_entity_display_build_alter core/lib/Drupal/Core/Entity/entity.api.php Alter the render array generated by an EntityDisplay for an entity.
hook_entity_field_values_init core/lib/Drupal/Core/Entity/entity.api.php Acts when initializing a fieldable entity object.
hook_entity_form_display_alter core/lib/Drupal/Core/Entity/entity.api.php Alter the settings used for displaying an entity form.
hook_entity_form_mode_alter core/lib/Drupal/Core/Entity/entity.api.php Change the form mode used to build an entity form.
hook_entity_insert core/lib/Drupal/Core/Entity/entity.api.php Respond to creation of a new entity.
hook_entity_load core/lib/Drupal/Core/Entity/entity.api.php Act on entities when loaded.
hook_entity_predelete core/lib/Drupal/Core/Entity/entity.api.php Act before entity deletion.
hook_entity_preload core/lib/Drupal/Core/Entity/entity.api.php Act on an array of entity IDs before they are loaded.
hook_entity_prepare_form core/lib/Drupal/Core/Entity/entity.api.php Acts on an entity object about to be shown on an entity form.
hook_entity_prepare_view core/lib/Drupal/Core/Entity/entity.api.php Act on entities as they are being prepared for view.
hook_entity_presave core/lib/Drupal/Core/Entity/entity.api.php Act on an entity before it is created or updated.
hook_entity_revision_create core/lib/Drupal/Core/Entity/entity.api.php Respond to entity revision creation.
hook_entity_revision_delete core/lib/Drupal/Core/Entity/entity.api.php Respond to entity revision deletion.
hook_entity_translation_create core/lib/Drupal/Core/Entity/entity.api.php Acts when creating a new entity translation.
hook_entity_translation_delete core/lib/Drupal/Core/Entity/entity.api.php Respond to entity translation deletion.
hook_entity_translation_insert core/lib/Drupal/Core/Entity/entity.api.php Respond to creation of a new entity translation.
hook_ENTITY_TYPE_build_defaults_alter core/lib/Drupal/Core/Entity/entity.api.php Alter entity renderable values before cache checking during rendering.
hook_ENTITY_TYPE_create core/lib/Drupal/Core/Entity/entity.api.php Acts when creating a new entity of a specific type.
hook_ENTITY_TYPE_delete core/lib/Drupal/Core/Entity/entity.api.php Respond to entity deletion of a particular type.
hook_ENTITY_TYPE_field_values_init core/lib/Drupal/Core/Entity/entity.api.php Acts when initializing a fieldable entity object.
hook_ENTITY_TYPE_insert core/lib/Drupal/Core/Entity/entity.api.php Respond to creation of a new entity of a particular type.
hook_ENTITY_TYPE_load core/lib/Drupal/Core/Entity/entity.api.php Act on entities of a specific type when loaded.
hook_ENTITY_TYPE_predelete core/lib/Drupal/Core/Entity/entity.api.php Act before entity deletion of a particular entity type.
hook_ENTITY_TYPE_prepare_form core/lib/Drupal/Core/Entity/entity.api.php Acts on a particular type of entity object about to be in an entity form.
hook_ENTITY_TYPE_presave core/lib/Drupal/Core/Entity/entity.api.php Act on a specific type of entity before it is created or updated.
hook_ENTITY_TYPE_revision_create core/lib/Drupal/Core/Entity/entity.api.php Respond to entity revision creation.
hook_ENTITY_TYPE_revision_delete core/lib/Drupal/Core/Entity/entity.api.php Respond to entity revision deletion of a particular type.
hook_ENTITY_TYPE_translation_create core/lib/Drupal/Core/Entity/entity.api.php Acts when creating a new entity translation of a specific type.
hook_ENTITY_TYPE_translation_delete core/lib/Drupal/Core/Entity/entity.api.php Respond to entity translation deletion of a particular type.
hook_ENTITY_TYPE_translation_insert core/lib/Drupal/Core/Entity/entity.api.php Respond to creation of a new entity translation of a particular type.
hook_ENTITY_TYPE_update core/lib/Drupal/Core/Entity/entity.api.php Respond to updates to an entity of a particular type.
hook_ENTITY_TYPE_view core/lib/Drupal/Core/Entity/entity.api.php Act on entities of a particular type being assembled before rendering.
hook_ENTITY_TYPE_view_alter core/lib/Drupal/Core/Entity/entity.api.php Alter the results of the entity build array for a particular entity type.
hook_entity_update core/lib/Drupal/Core/Entity/entity.api.php Respond to updates to an entity.
hook_entity_view core/lib/Drupal/Core/Entity/entity.api.php Act on entities being assembled before rendering.
hook_entity_view_alter core/lib/Drupal/Core/Entity/entity.api.php Alter the results of the entity build array.
hook_entity_view_display_alter core/lib/Drupal/Core/Entity/entity.api.php Alter the settings used for displaying an entity.
hook_entity_view_mode_alter core/lib/Drupal/Core/Entity/entity.api.php Change the view mode of an entity that is being displayed.
hook_node_search_result core/modules/node/node.api.php Act on a node being displayed as a search result.
hook_node_update_index core/modules/node/node.api.php Act on a node being indexed for searching.
hook_ranking core/modules/node/node.api.php Provide additional methods of scoring for core search results for nodes.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.