Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll

Introduction Entity API in Drupal

12/04/2025, by Ivan

The Drupal 8 Entity System

Entities are typed classes with methods.

Generic methods

$entity->id()

Entity type specific methods $node->getTitle()

Background

The Entity System was introduced late in the Drupal 7 development cycle with basic standards for loading entities. The added entity.module further expanded the API by adding support for saving and deleting entities, and many other enhancements.

Most of these improvements are now built into Drupal 8. Entity validation is now handled by its own API (which can validate an entity saved via REST instead of a form, for example).

Two Variants

Entity types in core come in two variants:

Configuration Entity
Uses the Configuration System. Supports translation and can provide default user settings for installs. Configuration entities are stored in the shared configuration database table as strings.

Content Entity
Consists of configurable and base fields, can have revisions, and supports translations. Content entities are stored in custom database tables as rows. The table name matches the entity "id," and the columns are defined via the entity’s “baseFieldDefinitions” method.

Bundles

Bundles are different variants of an entity type. For example, for the node entity type, bundles are different node types like "article" and "page".

Typically, a bundle is represented by a configuration entity, although other models exist in contrib modules. Thus, in the node example, the node type "article" itself is a configuration entity. The configuration stores differences between content entity types, such as settings and fields. When creating a new entity type with bundles, you will create both a content entity (handling content operations) and a configuration entity (managing bundle-specific differences).

Annotations

When creating a new entity type, you’ll need to use the annotation system built into core. Annotations look like docblock comments above a class but are parsed and cached by Drupal core. In many ways, annotations replace older Drupal 7 mechanisms.

Annotation Parser

Annotations are read and parsed at runtime by the annotation mechanism. Drupal 8 uses the Doctrine annotation parser, which turns them into usable PHP objects.

Syntax: Annotation syntax is enclosed in @ClassName(), mostly made of key/value pairs, and can include arrays using curly braces. Top-level keys do not need quotes, but array keys do. Each key/value pair should be on its own line and end with a comma. Functions can be applied to values, especially @Translation().

Invalid annotation syntax example:

/**
 * @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", Machine name for the entity type. Content & Config
label = @Translation("Node"), Human-readable name for the entity type. Content & Config
admin_permission = "administer nodes", Permission to administer the entity type. Content & Config
bundle_label = @Translation("Content type"), Optional human-readable name for the bundle type. Content
bundle_entity_type = "node_type", ID of the configuration entity for bundles. Content
base_table = "node", Database table for the entity type. Content
fieldable = TRUE, (boolean) Can the entity be extended via Field UI. Content
field_ui_base_route = "entity.node_type.edit_form", Base route for Field UI management. Content

Handlers

Handlers are defined in the entity annotation as an array. They delegate responsibilities to PHP classes for different lifecycle aspects of the entity.

  • storage - Manages loading, saving, and deleting. Content entities typically use SqlContentEntityStorage. Example: "storage" = "Drupal\node\NodeStorage"
  • form - Handles entity forms. You can define add, edit, delete, or use default for both add/edit.
  • view_builder - Manages display output. Example: "view_builder" = "Drupal\node\NodeViewBuilder"
  • list_builder - Handles the admin list of entities. Example: "list_builder" = "Drupal\node\NodeListBuilder"
  • route_provider - (Optional) Generates routes based on links. Works with the links annotation.
  • access - Provides access control. Typically you extend EntityAccessControlHandler.
  • views_data - Enables Views integration. Example: "views_data" = "Drupal\node\NodeViewsData"
  • storage_schema - Further defines database schema customization.
  • translation - Manages multilingual form behavior.

Links

Defined as an array in the entity annotation. These are URI paths for operations like viewing, editing, or deleting an entity.

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: Routes must still be defined using routing.yml or provided via route_provider.

Links and Route Provider

When combined with route_provider, the above links map to named routes like:

Link Key Route Name Example URI Description
canonical entity.node.canonical /node/1 View a specific node
add-page entity.node.add_page /node/add Select a node type to add
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 a specific node
delete-form entity.node.delete_form /node/1/delete Delete a specific node
collection entity.node.collection /admin/content View all nodes in a list

Usage: Access these links with toUrl() method:

$view_url_object = $entity->toUrl();  // Default is 'canonical'
$edit_url_string = $entity->toUrl('edit-form')->toString();

References:

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.