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
12/04/2025, by Ivan
  • Drupal 7 - Entities were generic stdClass objects.
  • Drupal 8 - Entities are now strongly typed objects, with each entity type defining the class used for its instances.

Requirements
Entity classes must reside in the Entity namespace of the module that provides the entity type, e.g., \Drupal\[module_name]\Entity. This means the PHP files for Entity classes can be found in the module's src/Entity directory.

The docblock for the class must include an EntityType annotation that defines metadata for entities of that type. This includes things like the entity type label, controllers, tables, and more. For a documented list of all available metadata properties, refer to \Drupal\Core\Entity\Annotation\EntityType.

Naming

Entity type names should be prefixed with the module name if the entity type name doesn't match the module name. The class name itself does not require the prefix, as it resides in the defining module's namespace, as long as the name is self-explanatory. For example, the entity type for taxonomy terms is taxonomy_term, and the class name is Drupal\taxonomy\Entity\Term.

Interfaces

Drupal 8 recommends using interfaces to type hint functions and methods instead of classes. For example, a generic Entity hook uses EntityInterface, such as hook_entity_insert(EntityInterface $entity), while a node-specific hook uses NodeInterface, such as hook_node_insert(NodeInterface $node).

Entity fields/properties are often very short, storage-oriented, and not highly descriptive. Also, content entities don’t use defined properties for their fields (including base fields like node title).

The recommended approach is to provide an interface with documented method names. A few rules to follow:

  • Methods typically use prefixes like get/set/is: getSomething(), setSomething($value), isSomething().
  • Add only methods for things that other code should change. For example, a node’s last changed date ($node->changed) shouldn’t be set directly, so we provide $node->getChangedTime() but not $node->setChangedTime().
  • Use self-descriptive method names. For example, $node->status uses $node->isPublished().

Clarity

To discover what entity types a module provides, check for classes in the module's Entity namespace that have the @EntityType annotation, which also includes the name in the annotation's id key.

When trying to find where a particular entity type is defined, the first thing to look for is the entity type prefix. If a module doesn't follow this naming convention, you can search for id = "$type". If the class or interface is known instead of the entity type, the namespace shows where it comes from.

Example

core/modules/node/src/Entity/Node.php:

namespace Drupal\node\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Session\AccountInterface;
use Drupal\node\NodeInterface;
use Drupal\user\UserInterface;

/**
 * Defines the node entity class.
 *
 * @ContentEntityType(
 *   id = "node",
 *   label = @Translation("Content"),
 *   bundle_label = @Translation("Content type"),
 *   handlers = {
 *     "storage" = "Drupal\node\NodeStorage",
 *     "storage_schema" = "Drupal\node\NodeStorageSchema",
 *     "view_builder" = "Drupal\node\NodeViewBuilder",
 *     "access" = "Drupal\node\NodeAccessControlHandler",
 *     "views_data" = "Drupal\node\NodeViewsData",
 *     "form" = {
 *       "default" = "Drupal\node\NodeForm",
 *       "delete" = "Drupal\node\Form\NodeDeleteForm",
 *       "edit" = "Drupal\node\NodeForm"
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\node\Entity\NodeRouteProvider",
 *     },
 *     "list_builder" = "Drupal\node\NodeListBuilder",
 *     "translation" = "Drupal\node\NodeTranslationHandler"
 *   },
 *   base_table = "node",
 *   data_table = "node_field_data",
 *   revision_table = "node_revision",
 *   revision_data_table = "node_field_revision",
 *   translatable = TRUE,
 *   list_cache_contexts = { "user.node_grants:view" },
 *   entity_keys = {
 *     "id" = "nid",
 *     "revision" = "vid",
 *     "bundle" = "type",
 *     "label" = "title",
 *     "langcode" = "langcode",
 *     "uuid" = "uuid",
 *     "status" = "status",
 *     "uid" = "uid",
 *   },
 *   bundle_entity_type = "node_type",
 *   field_ui_base_route = "entity.node_type.edit_form",
 *   common_reference_target = TRUE,
 *   permission_granularity = "bundle",
 *   links = {
 *     "canonical" = "/node/{node}",
 *     "delete-form" = "/node/{node}/delete",
 *     "edit-form" = "/node/{node}/edit",
 *     "version-history" = "/node/{node}/revisions",
 *     "revision" = "/node/{node}/revisions/{node_revision}/view",
 *   }
 * )
 */
class Node extends ContentEntityBase implements NodeInterface {
  // ...
}

To get the full picture of entities in Drupal 8, you can review the following diagram. This represents entity classes. For better visibility, open in a new tab:

classDrupal_Entities

 

Source URL:

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.