Introduction to Entity API in Drupal 8
This documentation needs review. See "Help improve this page" in the sidebar.
For the Drupal 7 Entity API: Go here.
Video Link: Entity Basics.
The Entity System is the API for entity manipulation (CRUD: create, read, update, delete). Entity validation has its own API (which could validate an Entity saved via REST, rather than a form, for example). While the Entity API is used for creating custom entity types, the Update API is used for modifying existing types.
The Drupal 8 entity system
Entities are typed classes with methods | |
---|---|
Generic methods | $entity->id() |
Entity type specific methods | $node->getTitle() |
Both types are defined and documented in interfaces.
Configuration Entity and Content Entity
Entity types in core come in two variants.
- Configuration Entity
- Used by the Configuration System. Supports translations and can provide custom defaults for installations. Configuration entities are stored within the common
config
database table as rows. - Content Entity
- Consist of configurable and base fields and can have revisions and support translations. Content entities are stored within a custom database table as rows. The table name is the same as the content entity "id", and the columns are defined by the entity's "baseFieldDefinitions" method.
Bundles
Bundles are variants of an entity type. For example, with the node entity type, the bundles are the different node types, such as 'article' and 'page'.
Typically, a bundle is represented by a Configuration Entity, though other models exist in contrib modules. So in the node example, the 'article' node type is itself a configuration entity. The configuration stores the differences between the content entity types, such as settings and fields. When creating a new entity type that has bundle entities, you will create both a Content Entity that will manage the content's details and operations, and a Configuration Entity that will handle the differences between the content entity types.
Annotations
When creating a new entity type, you'll need to use the annotations system built into core. Annotations look like docblock comments above the class, but are parsed and cached by Drupal core. In a lot of ways annotations replace some of the older style hooks used in Drupal 7.
Annotation parser
Annotations are read and parsed at runtime by an annotation engine. Drupal 8 uses the Doctrine annotation parser, which turns it into an object that PHP can use.
Syntax - The annotation syntax is surrounded with @ClassName()
, is predominantly made up of key/value pairs, and can contain arrays that use curly braces. Top level keys must not be surrounded with quotation marks, while array keys must. Each key/value pair should be on their own line, and that line should end with a comma. Certain functions can be executed on values, notably the @Translation()
function.
Non-working example of annotation syntax:
/**
* @ContentEntityType(
* id = "my_entity_type_id",
* label = @Translation("My entity type label"),
* example_pair = "this_examples_value",
* example_array = {
* "array_key" = "array_value",
* "some_other_key" = "some_other_value",
* },
* )
*/
Common top level annotations
Key = "Example Value", | Description | Entity Variant |
---|---|---|
id = "node", |
The machine name for the entity type. | Content & Config |
label = @Translation("Node"), |
The human-readable name for the entity type. | Content & Config |
admin_permission = "administer nodes", |
Permission that allows administrative access to configure and manage the entity type. This is necessary if your entity does not define an "access" handler. | Content & Config |
|
The optional human-readable name for the bundle entity type. | Content |
bundle_entity_type = "node_type", |
When creating a Content Entity that has bundles, this value should be the "id" of the Content Entity. In this case, "node_type" is a Content Entity. | Content |
base_table = "node", |
Database table name for the entity type. | Content |
|
The name of the route the Field UI is attached to on a fieldable entity. | Content |
Handlers
Handlers are defined in the entity annotation as an array. They support the entity by mapping certain parts of its execution to other PHP classes. Those classes will "handle" the assigned parts of the entity's execution.
Storage - Handles the loading, saving and deleting of the entity. By default, Content Entities use Drupal\Core\Entity\Sql\SqlContentEntityStorage
while Config Entities use Drupal\Core\Config\Entity\ConfigEntityStorage
. You can define a storage handler to extend the default storage methods of your entity. You may want to do this to provide additional methods for gathering entity revision ids or determining the number of translations an entity has.
Example: "storage" = "Drupal\node\NodeStorage",
Form - In any entity's handlers annotation there are multiple form handlers that map the entity's add, edit, and delete forms to other PHP classes.
Example:
"form" = {
"add" = "Drupal\block\BlockForm",
"edit" = "Drupal\block\BlockForm",
"delete" = "Drupal\block\Form\BlockDeleteForm",
}
Alternatively, you can define a "default" form to handle both the "add" and "edit" form instead of defining them separately. Worth noting is that the "delete" form will almost always be handled by a separate class from the other forms. This is because the delete form is generally a "confirmation form" that simply asks if the user is sure they want to delete the entity.
View builder - This handler provides a class that will handle the output of the entity when viewed by the end user. For example, when visiting a node on a Drupal 8 site, entity's output is handled by the NodeViewBuilder class.
Example: "view_builder" = "Drupal\node\NodeViewBuilder",
List builder - The list builder class will handle the list of entities for administrative purposes. This class will define the contents of the headers, rows, and operations when visiting the entities management page for the entity. For example, when visiting the /admin/content URI of your Drupal site, the table contents are provided by the Node entity's list builder class.
Example: "list_builder" = "Drupal\node\NodeListBuilder",
Route provider - An optional handler that, if implemented, generates routes for your entity management. Implementing this handler can replace the need for entity routes defined in the routing.yml file of your module. Note, the route_provider works in conjunction with the Links defined on your entity (see example below Links section). The route_provider annotation is an array.
Example:
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
}
Access - The access handler can be used to dynamically check permissions for your entity. It is a mapping to a class that implements the EntityAccessControlHandlerInterface
. Core provides an implementation of this interface as EntityAccessControlHandler
, but for reliable control over your entity you will want to extend that class with your own.
Example: "access" = "NodeAccessControlHandler",
Views data - The views_data handler allows an entity to extend the Views module with custom data provided by your entity. This can be for adding your entity's baseFieldDefinitions as Views fields, joining tables on entity relationships, or other Views related data alterations.
Example: "views_data" = "Drupal\node\NodeViewsData",
Storage schema - The storage_schema handler can be implemented to further alter the entity's database storage settings. For instance, adding additional table indexes.
Example: "storage_schema" = "Drupal\node\NodeStorageSchema",
Translation - The translation handler can be used to alter the way in which your entity forms interact with translations.
Example: "translation" = "Drupal\node\NodeTranslationHandler",
Full handlers example:
Drupal core provides handlers that you can use out of the box, but in many cases you'll want to extend these classes with your own for greater control and customization of your entity. This example shows a more-complete handlers annotation, using the core classes you can extend.
handlers = {
"view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
"list_builder" = "Drupal\Core\Entity\EntityListBuilder",
"access" = "Drupal\Core\Entity\EntityAccessControlHandler",
"views_data" = "Drupal\views\EntityViewsData",
"storage" = "Drupal\Core\Entity\Sql\SqlContentEntityStorage",
"storage_schema" = "Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema",
"translation" = "Drupal\content_translation\ContentTranslationHandler",
"form" = {
"default" = "Drupal\Core\Entity\ContentEntityForm",
"add" = "Drupal\Core\Entity\ContentEntityForm",
"edit" = "Drupal\Core\Entity\ContentEntityForm",
"delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
},
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
},
},
Links
Links are defined in the entity annotation with the array syntax. Links have a specific set of keys whose value are URIs where the entity type or single entities of that type can be managed. Both Content and Configuration Entities can have these links defined.
Example:
id = "node",
handlers = {
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider"
}
},
links = {
"canonical" = "/node/{node}",
"add-page" = "/node/add",
"add-form" = "/node/add/{node_type}",
"edit-form" = "/node/{node}/edit",
"delete-form" = "/node/{node}/delete",
"collection" = "/admin/content",
},
Note, this is not taken verbatim from the Node module, it is only an example.
Creating these links does not automatically create the routes for those URIs. To make these links accessible, your module will need to implement its own routing.yml file, or use a "route_provider" handler in the entity annotation.
Links & Route Provider
The above links working together with a "route_provider" will make the following named routes available to Drupal.
Links Key | Route Name | Route Example URI | Description |
---|---|---|---|
canonical | entity.node.canonical | /node/1 | View a specific node |
add-page | entity.node.add_page | /node/add | Select bundle of node to be added |
add-form | entity.node.add_form | /node/add/article | Add a node (of a specific bundle) |
edit-form | entity.node.edit_form | /node/1/edit | Edit form for a specific node |
delete-form | entity.node.delete_form | /node/1/delete | Delete form for a specific node |
collection | entity.node.collection | /admin/content | View all nodes as a list |
Using Links
These links can be accessed with an entity's toUrl() method:
$view_url_object = $entity->toUrl(); // Default is 'canonical'
$edit_url_string = $entity->toUrl('edit-form')->toString();
References:
- Entity API - Generated documentation.
- Creating a custom content entity - Very simple custom entity.
- Creating a content entity type in Drupal 8 - Extended example with handlers, permissions, routing, and links.
- Creating a configuration entity type in Drupal 8 - Extended example with handlers, routing, and schema.
- [External] Entity Type Walkthrough - documentation for general entity type in-practice purpose
This page is still a stub, documentation is being written while the Entity API is finalized for Drupal 8.
TODO/Planned:
-Handlers
- -Storage
- -Storage Schema
- -Access
- -Forms
- -View Builder
- -Views Data
- -List Builder
-Bundles
- Configuration entities
- Content entities
- - Revisions
- - Translations
- - Fields
- UUID support
- Entity query
-RoutingDrupal 8 Entity API issues being worked on:
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion