logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动
30/09/2025, by Ivan
  • Drupal 7 - 实体是通用的 stdClass 对象。
  • Drupal 8 - 实体现在是具有特定类型的对象,每种实体类型都会定义一个类,用于该实体的实例。

要求
实体类必须放置在提供该实体类型的模块的 Entity 子命名空间中,例如 \Drupal\[module_name]\Entity。这意味着实体类的 PHP 文件可以在模块的 src/Entity 目录下找到。

类的文档块必须包含 EntityType 注解,它定义了该类型实体的元数据。这些包括实体类型的标签、控制器、表等。有关所有可用元数据属性的文档化列表,请参阅 \Drupal\Core\Entity\Annotation\EntityType 类。

命名

如果实体类型与模块名称不一致,则实体类型的名称必须带有模块名前缀。实体类型类本身不需要加前缀,因为它位于定义模块的命名空间中,只要类名本身足够清晰。例如,分类术语的实体类型称为 taxonomy_term,而类名为 Drupal\taxonomy\Entity\Term。

接口

Drupal 8 推荐使用接口而不是类来进行函数类型提示和方法调用。例如,通用实体存储使用 EntityInterface 作为类型提示,就像在 hook_entity_insert(EntityInterface $entity) 中一样;而节点存储使用 NodeInterface 作为类型提示,就像在 hook_node_insert(NodeInterface $node) 中一样。

实体的字段/属性通常很简短,偏向存储,并且不太具有可读性。此外,内容实体通常不会使用特定属性来表示其字段(包括基本字段,如节点标题)。

因此,推荐的方法是提供一个带有文档化方法名称的接口。在此过程中应遵循以下规则:

  • 方法通常以 get/set/is 或类似前缀开头:getSomething()、setSomething($value)、isSomething()。
  • 仅为需要被其他代码修改的内容添加方法。例如,节点的最后修改时间($node->updated)不应被修改,因此有 $node->getChangedTime() 方法,但没有 $node->setChangedTime() 方法。
  • 使用自解释的方法名称,例如访问 $node->status 的方法是 $node->isPublished()。

可理解性

要了解某个模块提供了哪些实体类型,请查看该模块 Entity 命名空间下的类,这些类带有 @EntityType 注解,该注解还包含一个 id 键用于标识实体类型名称。

当试图查找某个实体类型定义在哪里时,首先要查找的是实体类型的前缀。如果模块没有遵循命名约定,可以通过搜索 id = "$type" 来找到。如果已知类或接口而不是实体类型,那么命名空间就能指示它的来源。

示例

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;

/**
 * 定义 Node 实体类。
 *
 * @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 {
  // ...
}

要全面了解 Drupal 8 中的实体,我们可以参考以下图表。它展示了实体类。查看时可在新标签页中打开:

classDrupal_Entities