Entity API in Drupal 9
Same name and namespace in other branches
- 8 core/lib/Drupal/Core/Entity/entity.api.php \entity_api
Describes how to define and manipulate content and configuration entities.
Entities, in Drupal, are objects that are used for persistent storage of content and configuration information. See the Information types topic for an overview of the different types of information, and the Configuration API topic for more about the configuration API.
Each entity is an instance of a particular "entity type". Some content entity types have sub-types, which are known as "bundles", while for other entity types, there is only a single bundle. For example, the Node content entity type, which is used for the main content pages in Drupal, has bundles that are known as "content types", while the User content type, which is used for user accounts, has only one bundle.
The sections below have more information about entities and the Entity API; for more detailed information, see https://www.drupal.org/developing/api/entity.
Defining an entity type
Entity types are defined by modules, using Drupal's Plugin API (see the Plugin API topic for more information about plugins in general). Here are the steps to follow to define a new entity type:
- Choose a unique machine name, or ID, for your entity type. This normally starts with (or is the same as) your module's machine name. It should be as short as possible, and may not exceed 32 characters.
- Define an interface for your entity's get/set methods, usually extending either \Drupal\Core\Config\Entity\ConfigEntityInterface or \Drupal\Core\Entity\ContentEntityInterface.
- Define a class for your entity, implementing your interface and extending either \Drupal\Core\Config\Entity\ConfigEntityBase or \Drupal\Core\Entity\ContentEntityBase, with annotation for @ConfigEntityType or @ContentEntityType in its documentation block. If you are defining a content entity type, it is recommended to extend the \Drupal\Core\Entity\EditorialContentEntityBase base class in order to get out-of-the-box support for Entity API's revisioning and publishing features, which will allow your entity type to be used with Drupal's editorial workflow provided by the Content Moderation module.
- In the annotation, the 'id' property gives the entity type ID, and the 'label' property gives the human-readable name of the entity type. If you are defining a content entity type that uses bundles, the 'bundle_label' property gives the human-readable name to use for a bundle of this entity type (for example, "Content type" for the Node entity).
- The annotation will refer to several handler classes, which you will also
need to define:
- list_builder: Define a class that extends \Drupal\Core\Config\Entity\ConfigEntityListBuilder (for configuration entities) or \Drupal\Core\Entity\EntityListBuilder (for content entities), to provide an administrative overview for your entities.
- add and edit forms, or default form: Define a class (or two) that extend(s) \Drupal\Core\Entity\EntityForm to provide add and edit forms for your entities. For content entities, base class \Drupal\Core\Entity\ContentEntityForm is a better starting point.
- delete form: Define a class that extends \Drupal\Core\Entity\EntityConfirmFormBase to provide a delete confirmation form for your entities.
- view_builder: For content entities and config entities that need to be viewed, define a class that implements \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
- translation: For translatable content entities (if the 'translatable' annotation property has value TRUE), define a class that extends \Drupal\content_translation\ContentTranslationHandler, to translate the content. Configuration translation is handled automatically by the Configuration Translation module, without the need of a handler class.
- access: If your configuration entity has complex permissions, you might need an access control handling, implementing \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most entities can just use the 'admin_permission' annotation property instead. Note that if you are creating your own access control handler, you should override the checkAccess() and checkCreateAccess() methods, not access().
- storage: A class implementing \Drupal\Core\Entity\EntityStorageInterface. If not specified, content entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and config entities will use \Drupal\Core\Config\Entity\ConfigEntityStorage. You can extend one of these classes to provide custom behavior.
- views_data: A class implementing \Drupal\views\EntityViewsDataInterface to provide views data for the entity type. You can autogenerate most of the views data by extending \Drupal\views\EntityViewsData.
- For content entities, the annotation will refer to a number of database tables and their fields. These annotation properties, such as 'base_table', 'data_table', 'entity_keys', etc., are documented on \Drupal\Core\Entity\EntityType.
- For content entities that are displayed on their own pages, the annotation will refer to a 'uri_callback' function, which takes an object of the entity interface you have defined as its parameter, and returns routing information for the entity page; see node_uri() for an example. You will also need to add a corresponding route to your module's routing.yml file; see the entity.node.canonical route in node.routing.yml for an example, and see Entity routes below for some notes.
- Optionally, instead of defining routes, routes can be auto generated by
providing a route handler. See Entity routes. Otherwise, define routes
and links for the various URLs associated with the entity.
These go into the 'links' annotation, with the link type as the key, and
the path of this link template as the value. The corresponding route
requires the following route name:
"entity.$entity_type_id.$link_template_type". See Entity routes below for
some routing notes. Typical link types are:
- canonical: Default link, either to view (if entities are viewed on their own pages) or edit the entity.
- delete-form: Confirmation form to delete the entity.
- edit-form: Editing form.
- Other link types specific to your entity type can also be defined.
- If your content entity is fieldable, provide the 'field_ui_base_route' annotation property, giving the name of the route that the Manage Fields, Manage Display, and Manage Form Display pages from the Field UI module will be attached to. This is usually the bundle settings edit page, or an entity type settings page if there are no bundles.
- If your content entity has bundles, you will also need to define a second plugin to handle the bundles. This plugin is itself a configuration entity type, so follow the steps here to define it. The machine name ('id' annotation property) of this configuration entity class goes into the 'bundle_entity_type' annotation property on the entity type class. For example, for the Node entity, the bundle class is \Drupal\node\Entity\NodeType, whose machine name is 'node_type'. This is the annotation property 'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the bundle config entity type annotation must have a 'bundle_of' property, giving the machine name of the entity type it is acting as a bundle for. These machine names are considered permanent, they may not be renamed.
- Additional annotation properties can be seen on entity class examples such as \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role (configuration). These annotation properties are documented on \Drupal\Core\Entity\EntityType.
Entity routes
Entity routes can be defined in *.routing.yml files, like any other route: see the Routing API topic for more information. Another option for entity routes is to use a route provider class, and reference it in the annotations on the entity class: see the end of this section for an example.
It's possible to use both a YAML file and a provider class for entity routes, at the same time. Avoid duplicating route names between the two: if a duplicate route name is found in both locations, the one in the YAML file takes precedence; regardless, such duplication can be confusing.
Here's an example YAML route specification, for the block configure form:
entity.block.edit_form:
path: '/admin/structure/block/manage/{block}'
defaults:
_entity_form: 'block.default'
_title: 'Configure block'
requirements:
_entity_access: 'block.update'
Some notes on this example:
- path: The {block} in the path is a placeholder, which (for an entity) must always take the form of {machine_name_of_entity_type}. In the URL, the placeholder value will be the ID of an entity item. When the route is used, the entity system will load the corresponding entity item and pass it in as an object to the controller for the route.
- defaults: For entity form routes, use _entity_form rather than the generic _controller or _form. The value is composed of the entity type machine name and a form handler type from the entity annotation (see Defining an entity type above for more on handlers and annotation). So, in this example, block.default refers to the 'default' form handler on the block entity type, whose annotation contains:
handlers = {
"form" = {
"default" = "Drupal\block\BlockForm",
If instead of YAML you want to use a route provider class:
- \Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider provides canonical, edit-form, and delete-form routes.
- \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider provides the same routes, set up to use the administrative theme for edit and delete pages.
- You can also create your own class, extending one of these two classes if you only want to modify their behavior slightly.
To register any route provider class, add lines like the following to your entity class annotation:
handlers = {
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
Defining a content entity bundle
For entity types that use bundles, such as Node (bundles are content types) and Taxonomy (bundles are vocabularies), modules and install profiles can define bundles by supplying default configuration in their config/install directories. (See the Configuration API topic for general information about configuration.)
There are several good examples of this in Drupal Core:
- The Forum module defines a content type in node.type.forum.yml and a vocabulary in taxonomy.vocabulary.forums.yml
- The Book module defines a content type in node.type.book.yml
- The Standard install profile defines Page and Article content types in node.type.page.yml and node.type.article.yml, a Tags vocabulary in taxonomy.vocabulary.tags.yml, and a Node comment type in comment.type.comment.yml. This profile's configuration is especially instructive, because it also adds several fields to the Article type, and it sets up view and form display modes for the node types.
Loading, querying, and rendering entities
To load entities, use the entity storage manager, which is an object implementing \Drupal\Core\Entity\EntityStorageInterface that you can retrieve with:
$storage = \Drupal::entityTypeManager()
->getStorage('your_entity_type');
// Or if you have a $container variable:
$storage = $container
->get('entity_type.manager')
->getStorage('your_entity_type');
Here, 'your_entity_type' is the machine name of your entity type ('id' annotation property on the entity class), and note that you should use dependency injection to retrieve this object if possible. See the Services and Dependency Injection topic for more about how to properly retrieve services.
To query to find entities to load, use an entity query, which is an object implementing \Drupal\Core\Entity\Query\QueryInterface that you can retrieve with:
// Simple query:
$query = \Drupal::entityQuery('your_entity_type');
// Or, if you have a $container variable:
$storage = $container
->get('entity_type.manager')
->getStorage('your_entity_type');
$query = $storage
->getQuery();
If you need aggregation, there is an aggregate query available, which implements \Drupal\Core\Entity\Query\QueryAggregateInterface:
$query \Drupal::entityQueryAggregate('your_entity_type');
// Or:
$query = $storage->getAggregateQuery('your_entity_type');
In either case, you can then add conditions to your query, using methods like condition(), exists(), etc. on $query; add sorting, pager, and range if needed, and execute the query to return a list of entity IDs that match the query.
Here is an example, using the core File entity:
$fids = Drupal::entityQuery('file')
->condition('status', \Drupal\file\FileInterface::STATUS_PERMANENT, '<>')
->condition('changed', REQUEST_TIME - $age, '<')
->range(0, 100)
->execute();
$files = $storage
->loadMultiple($fids);
The normal way of viewing entities is by using a route, as described in the sections above. If for some reason you need to render an entity in code in a particular view mode, you can use an entity view builder, which is an object implementing \Drupal\Core\Entity\EntityViewBuilderInterface that you can retrieve with:
$view_builder = \Drupal::entityTypeManager()
->getViewBuilder('your_entity_type');
// Or if you have a $container variable:
$view_builder = $container
->get('entity_type.manager')
->getViewBuilder('your_entity_type');
Then, to build and render the entity:
// You can omit the language ID, by default the current content language will
// be used. If no translation is available for the current language, fallback
// rules will be used.
$build = $view_builder
->view($entity, 'view_mode_name', $language
->getId());
// $build is a render array.
$rendered = \Drupal::service('renderer')
->render($build);
Access checking on entities
Entity types define their access permission scheme in their annotation. Access permissions can be quite complex, so you should not assume any particular permission scheme. Instead, once you have an entity object loaded, you can check for permission for a particular operation (such as 'view') at the entity or field level by calling:
$entity
->access($operation);
$entity->nameOfField
->access($operation);
The interface related to access checking in entities and fields is \Drupal\Core\Access\AccessibleInterface.
The default entity access control handler invokes two hooks while checking access on a single entity: hook_entity_access() is invoked first, and then hook_ENTITY_TYPE_access() (where ENTITY_TYPE is the machine name of the entity type). If no module returns a TRUE or FALSE value from either of these hooks, then the entity's default access checking takes place. For create operations (creating a new entity), the hooks that are invoked are hook_entity_create_access() and hook_ENTITY_TYPE_create_access() instead.
The access to an entity can be influenced in several ways:
- To explicitly allow access, return an AccessResultInterface object with
isAllowed() returning TRUE. Other modules can override this access by returning TRUE for isForbidden().
- To explicitly forbid access, return an AccessResultInterface object with
isForbidden() returning TRUE. Access will be forbidden even if your module (or another module) also returns TRUE for isNeutral() or isAllowed().
- To neither allow nor explicitly forbid access, return an
AccessResultInterface object with isNeutral() returning TRUE.
- If your module does not return an AccessResultInterface object, neutral
access will be assumed.
The Node entity type has a complex system for determining access, which developers can interact with. This is described in the Node access topic.
See also
Entity CRUD, editing, and view hooks
\Drupal\Core\Entity\EntityRepositoryInterface::getTranslationFromContext()
File
- core/
lib/ Drupal/ Core/ Entity/ entity.api.php, line 326 - Hooks and documentation related to entities.
Functions
Name | Location | Description |
---|---|---|
hook_entity_access |
core/ |
Control entity operation access. |
hook_entity_create_access |
core/ |
Control entity create access. |
hook_ENTITY_TYPE_access |
core/ |
Control entity operation access for a specific entity type. |
hook_ENTITY_TYPE_create_access |
core/ |
Control entity create access for a specific entity type. |
Classes
Name | Location | Description |
---|---|---|
ConfigEntityBase |
core/ |
Defines a base configuration entity class. |
ConfigEntityListBuilder |
core/ |
Defines the default class to build a listing of configuration entities. |
ConfigEntityStorage |
core/ |
Defines the storage class for configuration entities. |
ConfigEntityType |
core/ |
Defines a config entity type annotation object. |
ContentEntityBase |
core/ |
Implements Entity Field API specific enhancements to the Entity class. |
ContentEntityType |
core/ |
Defines a content entity type annotation object. |
ContentTranslationHandler |
core/ |
Base class for content translation handlers. |
EditorialContentEntityBase |
core/ |
Provides a base entity class with extended revision and publishing support. |
EntityConfirmFormBase |
core/ |
Provides a generic base class for an entity-based confirmation form. |
EntityDeleteForm |
core/ |
Provides a generic base class for an entity deletion form. |
EntityForm |
core/ |
Base class for entity forms. |
EntityHandlerBase |
core/ |
Provides a base class for entity handlers. |
EntityListBuilder |
core/ |
Defines a generic implementation to build a listing of entities. |
EntityType |
core/ |
Provides an implementation of an entity type and its metadata. |
EntityType |
core/ |
Defines an Entity type annotation object. |
EntityViewBuilder |
core/ |
Base class for entity view builders. |
RevisionableContentEntityBase |
core/ |
Provides a content entity with extended support for revisions. |
SqlContentEntityStorage |
core/ |
A content entity database storage implementation. |
Interfaces
Name | Location | Description |
---|---|---|
AccessibleInterface |
core/ |
Interface for checking access. |
ConfigEntityInterface |
core/ |
Defines a common interface for configuration entities. |
ContentEntityInterface |
core/ |
Defines a common interface for all content entity objects. |
EntityHandlerInterface |
core/ |
Defines an interface for entity handlers. |
EntityInterface |
core/ |
Defines a common interface for all entity objects. |
EntityStorageInterface |
core/ |
Defines the interface for entity storage classes. |
EntityViewBuilderInterface |
core/ |
Defines an interface for entity view builders. |
FieldableEntityInterface |
core/ |
Interface for entities having fields. |
Traits
Name | Location | Description |
---|---|---|
EntityDeleteFormTrait |
core/ |
Provides a trait for an entity deletion form. |
RevisionLogEntityTrait |
core/ |
Provides a trait for accessing revision logging and ownership information. |
SynchronizableEntityTrait |
core/ |
Provides a trait for accessing synchronization information. |