Drupal 8 中的 Entity API 简介
Drupal 8 实体系统
实体是带有方法的强类型类
| 通用方法 |
$entity->id() |
| 特定于实体类型的方法 | $node->getTitle() |
背景
实体系统在 Drupal 7 开发周期的后期引入,带有基本的实体加载标准。entity.module 进一步扩展了 API,增加了对保存和删除实体的支持,以及许多其他改进。
这些改进中的大多数现在已包含在 Drupal 8 中。实体验证现在在其自己的 API 中进行(例如,它可以验证通过 REST 保存的实体,而不是通过表单)。
两种变体
核心中的实体类型有两种变体。
配置实体
使用 配置系统。支持翻译,并且可以为安装提供默认的用户设置。配置对象以行的形式存储在共享的配置数据库表中。
内容实体
由可配置字段和基本字段组成,可以有修订并支持翻译。内容对象以行的形式存储在自定义数据库表中。表名与对象的“id”相同,列由对象的“baseFieldDefinitions”方法定义。
包(Bundles)
包是实体类型的不同变体。例如,对于节点实体类型,包是不同的节点类型,如“文章”和“页面”。
通常,包由配置对象表示,尽管 contrib 模块中也有其他模型。因此,在节点示例中,“article” 节点类型本身就是一个配置对象。配置存储了不同内容实体类型之间的差异,如设置和字段。当您创建一个带包的实体类型时,您将同时创建一个内容实体(用于处理内容的详细信息和操作)和一个配置对象(用于处理内容实体类型之间的差异)。
注解
创建新的实体类型时,您需要使用内置在核心中的注解系统。注解看起来像类上方的 docblock 注释,但会被 Drupal 核心解析和缓存。在许多方面,注解替代了 Drupal 7 中使用的一些较旧风格。
注解解析器
注解在运行时由注解机制读取和解析。Drupal 8 使用 Doctrine 注解解析器,将其转换为 PHP 可以使用的对象。
语法。 注解语法用 @ClassName() 包裹,主要由 key/value 键值对组成,并且可以包含使用大括号的数组。顶层键不需要加引号,而数组键必须加引号。每个 key/value 键值对必须在单独的一行上,并且这一行必须以逗号结尾。某些函数可以对值执行,特别是 @Translation() 函数。
注解语法的一个无效示例:
/**
* @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",
* },
* )
*/
常见的顶层注解
| Key = "示例值" | 描述 | 实体变体 |
| id = "node", |
对象类型的机器名。 |
内容 & 配置 |
| label = @Translation("Node"), |
对象类型的可读名称。 |
内容 & 配置 |
| admin_permission = "administer nodes", |
允许管理访问以配置和管理对象类型的权限。如果您的实体未定义“访问”处理器,则这是必需的。 |
内容 & 配置 |
| bundle_label = @Translation("Content type"), |
包对象类型的可选可读名称。 |
内容 |
| bundle_entity_type = "node_type", |
当创建带包的内容对象时,此值必须是配置对象的“id”。在本例中,“node_type”是配置对象。 |
内容 |
| base_table = "node", |
对象类型的数据库表名。 |
内容 |
| fieldable = TRUE, |
(布尔值)此实体类型是否可以通过字段 UI 扩展。 |
内容 |
| field_ui_base_route = "entity.node_type.edit_form", | 绑定到实体可用对象的字段 UI 的路由名称。 | 内容 |
处理器(Handlers)
处理器在对象的注解中定义为数组。它们支持实体,将实体执行的特定部分映射到其他 PHP 类。这些类将“处理”实体执行的指定部分。
存储 - 处理对象的加载、保存和删除。默认情况下,内容对象使用 Drupal\Core\Entity\Sql\SqlContentEntityStorage,而配置对象使用 Drupal\Core\Config\Entity\ConfigEntityStorage。您可以定义一个存储处理器,以扩展您的实体的标准存储方法。您可能希望这样做,以便提供额外的方法来收集实体修订 ID 或确定实体拥有的翻译数量。
示例: "storage" = "Drupal\node\NodeStorage",
表单 - 在任何实体的处理器注解中都有几个表单处理器,它们将添加、编辑和删除实体的表单映射到其他 PHP 类。
示例:
"form" = {
"add" = "Drupal\block\BlockForm",
"edit" = "Drupal\block\BlockForm",
"delete" = "Drupal\block\Form\BlockDeleteForm",
}
此外,您可以定义一个“默认”表单来处理“添加”和“编辑”表单,而不是分别定义它们。值得注意的是,“删除”表单几乎总是由与其他表单不同的类处理。这是因为删除表单通常是一个“确认表单”,它只会询问用户是否确定要删除对象。
视图构建器(View builder) - 此处理器提供一个类,该类将在最终用户查看实体时处理其输出。例如,在 Drupal 8 站点上访问一个节点时,实体的输出由 NodeViewBuilder 类处理。
示例: "view_builder" = "Drupal\node\NodeViewBuilder",
列表构建器(List builder) - 列表构建器类将处理实体的管理列表。该类将在访问实体管理页面时定义表头、行和操作的内容。例如,在访问 Drupal 站点的 URI /admin/content 时,表格内容由节点实体的列表构建器类提供。
示例: "list_builder" = "Drupal\node\NodeListBuilder",
路由提供器(Route provider) - 可选的处理器,如果实现,它将为管理对象生成路由。实现此处理器可能取代您模块 routing.yml 文件中定义的实体路径。请注意,route_provider 与为您的实体定义的链接一起工作(参见下文“链接”部分的示例)。route_provider 注解是一个数组。
示例:
"route_provider" = {
"html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
}
访问(Access) - 访问处理器可用于动态检查您的实体的权限。它映射到一个实现 EntityAccessControlHandlerInterface 的类。核心提供了该接口的实现 EntityAccessControlHandler,但如果您希望对实体进行更严格的控制,则需要扩展该类并自定义。
示例: "access" = "NodeAccessControlHandler",
视图数据(Views data) - views_data 处理器允许对象为 Views 模块扩展您的实体提供的自定义数据。这可能用于将实体的 baseFieldDefinitions 添加为视图字段、在实体关系上连接表,或进行与视图相关的其他数据更改。
示例: "views_data" = "Drupal\node\NodeViewsData",
存储模式(Storage schema) - storage_schema 处理器可用于进一步更改对象的数据库存储设置。例如,添加额外的表索引。
示例: "storage_schema" = "Drupal\node\NodeStorageSchema",
翻译(Translation) - 翻译处理器可用于更改实体表单与翻译的交互方式。
示例: "translation" = "Drupal\node\NodeTranslationHandler",
完整 handlers 示例:
Drupal 核心提供了开箱即用的处理器,但在许多情况下,您会希望扩展这些类以便对您的实体有更多控制和自定义。以下示例展示了使用核心类的更完整的处理器注解,您可以扩展这些类。
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)
链接在对象注解中以数组语法定义。链接有一组特定的键,其值是用于管理对象类型或该类型单个对象的 URI。这些对象可以为内容或配置对象定义。
示例:
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",
},
请注意,这不是从 Node 模块逐字提取的,这只是一个示例。
创建这些链接不会自动为这些 URI 创建路由。为了使这些链接可用,您的模块需要实现自己的 routing.yml 文件,或在对象注解中使用 route_provider 处理器。
链接和路由提供器
上述链接与“route_provider”一起使用时,将使以下命名路由在 Drupal 中可用。
| 链接键 | 路由名称 | 示例路由 URI | 描述 |
| canonical | entity.node.canonical | /node/1 | 查看特定节点 |
| add-page | entity.node.add_page | /node/add | 选择要添加的节点 |
| add-form | entity.node.add_form | /node/add/article | 添加节点(特定包) |
| edit-form | entity.node.edit_form | /node/1/edit | 编辑特定节点的表单 |
| delete-form | entity.node.delete_form | /node/1/delete | 删除特定节点的表单 |
| collection | entity.node.collection | /admin/content | 以列表形式查看所有节点 |
使用链接
这些链接可以通过对象的 toUrl() 方法访问:
$view_url_object = $entity->toUrl(); // 默认是 'canonical'
$edit_url_string = $entity->toUrl('edit-form')->toString();
参考资料:
- Entity API - 自动生成的文档。
- 创建自定义内容实体 - 一个非常简单的自定义对象。
- 在 Drupal 8 中创建内容实体类型 - 包含处理器、权限、路由和链接的高级示例。
- 在 Drupal 8 中创建配置对象类型 - 包含处理器、路由和模式的高级示例。
- [外部] Entity Type Walkthrough - 通用实体类型的实践文档