logo

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

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

演示 EBT 模块 下载 EBT 模块

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

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

演示 EPT 模块 滚动

滚动
30/09/2025, by Ivan

目标受众

本文件主要面向具有面向对象 PHP、Drupal 6 或 Drupal 7 编程经验的开发人员,以及希望学习 Drupal 8 原理的人。

Drupal 8 内容实体类型创建文档 包含完整的可用选项列表。

在 Drupal 8 中构建无绑定的内容类型

在这种情况下,我们创建一个 Drupal 8 内容对象,它没有任何绑定。

该实体没有实现字段 API,因此它仅存在于代码中。不过,这可以作为构建内容实体的有用框架,因为我们稍后会导入更复杂的数据。

最后,当涉及到一些面向对象编程(OOP)的概念时,我会引用相关文档。

背景

我们的模块叫做“advertiser”。
我们的内容实体类型也叫做“advertiser”。

我们新的 Drupal 8 advertiser 内容实体将拥有以下字段:
- UUID
- ID

在 Drupal 8 中定义对象内容

首先,所有自定义对象现在都放在 modules/custom 中,而不是放在 sites/all/modules/custom。

在我们的自定义对象中,最终得到的文件结构如下:

modules/custom/advertiser$
├── advertiser.info.yml
└── src
    └── Entity
         └── Advertiser.php

作为参考,你可以查看 现成的 Advertiser 实体,其中包括额外的补充,如测试和约束插件,但目前我们会保持简单。

Info 文件

我们将从在 module_name.info.yml 中定义自定义模块开始。这很直观:

name: Advertiser
type: module
description: 'Barebones advertiser entity'
package: custom
core: 8.x

实体骨架

与此同时,Advertiser 实体的基类和相关模式在 src/Entity/Advertiser.php 中定义。

我们首先为 Advertiser 实体类定义 命名空间。当我们想要使用类时,这会很有用。

namespace Drupal\advertiser\Entity;

现在是时候定义我们的实体了,这是通过注解完成的。

这是对象类型的实际定义,系统会读取并缓存它,因此在任何更改后一定要清除缓存。
<?php
/**
  * 定义 Advertiser 实体。
  *
  * @ingroup advertiser
  *
  * @ContentEntityType(
  *   id = "advertiser",
  *   label = @Translation("Advertiser"),
  *   base_table = "advertiser",
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
  *   },
  * )
  */
?>

由于这是一个空实体,我们只使用少量属性,而不是像 Access 模块那样的处理程序。

我们已经有了一个功能模块,它定义了自定义内容实体,但我们会发现数据库中没有创建“advertiser”表。

$ drush sql-cli
mysql> SHOW TABLES;

这是因为在我们的类中没有显式与数据库交互的方法。此外,我们还需要定义一组最基本的方法,以便实体能与数据库进行正常交互。

ContentEntityBase

通常我们可以在脚本顶部的命名空间定义之后添加类似 use Drupal\Core\Entity\ContentEntityBase; 的代码,从而引入类。这会使这些方法可用于我们的类,类可以扩展这些方法,或者在接口的情况下实现它们。

我们做两件事:扩展现有的 ContentEntityBase 类,它已经有了与数据库交互所需的方法;同时实现 ContentEntityInterface 来描述……

我们需要访问数据库的方法。这并不描述我们是如何实现的,而是由实现类来完成。我们可以根据需要多次实现这个接口,用不同的方式实现。然后我们就可以在不影响代码的情况下在不同实现之间切换,因为接口定义了如何使用它,而不管其具体实现方式。- https://secure.php.net/manual/en/language.oop5.interfaces.php

这意味着我们会得到如下定义:别忘了在脚本顶部通过 use 引入任何新类:

class Advertiser extends ContentEntityBase implements ContentEntityInterface {

但我们仍需要使用这些新方法向数据库写入内容,我们从为实体定义基本字段开始。

baseFieldDefinitions

baseFieldDefinitions 方法来源于我们扩展的 ContentEntityBase 类。
它需要一个参数:

对象类型的定义。当一个类用于多个(可能是动态的)对象类型时很有用。

它返回:

基于字段名称的对象类型的基本字段定义数组。

因此我们这样实现:

<?php
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
      
    // 标准字段,作为主键时唯一。
    $fields['id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('Advertiser 实体的 ID。'))
      ->setReadOnly(TRUE);

    // 标准字段,在当前项目范围之外唯一。
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('Advertiser 实体的 UUID。'))
      ->setReadOnly(TRUE);
      
    return $fields;
  }
?>

需要注意:

BaseFieldDefinitions

“为对象类型提供基本字段定义”。

- 它是 FieldableEntityInterface 的一个公共静态方法。

BaseFieldDefinition

“用于定义实体字段的类”。

- 提供我们所需的所有方法,例如 创建 字段、添加约束 等。

完整实体

所以整体来看,我们的代码如下:

<?php

namespace Drupal\advertiser\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\ContentEntityInterface;

/**
 * 定义 Advertiser 实体。
 *
 * @ingroup advertiser
 *
 * @ContentEntityType(
 *   id = "advertiser",
 *   label = @Translation("advertiser"),
 *   base_table = "advertiser",
 *   entity_keys = {
 *     "id" = "id",
 *     "uuid" = "uuid",
 *   },
 * )
 */

class Advertiser extends ContentEntityBase implements ContentEntityInterface {

  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {

    // 标准字段,作为主键时唯一。
    $fields['id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('ID'))
      ->setDescription(t('Advertiser 实体的 ID。'))
      ->setReadOnly(TRUE);

    // 标准字段,在当前项目范围之外唯一。
    $fields['uuid'] = BaseFieldDefinition::create('uuid')
      ->setLabel(t('UUID'))
      ->setDescription(t('Advertiser 实体的 UUID。'))
      ->setReadOnly(TRUE);

    return $fields;
  }
}
?>

安装模块后,你应该会看到数据库中新增了“advertiser”表!

$ drush sql-cli
mysql> SHOW TABLES;

或者

drush sqlq "show tables like 'advertiser'"
drush sqlq "describe advertiser"

如果模块已经安装,你需要执行实体更新。

在 #2976035: 对象类型的 CRUD 操作应使用最新安装的对象类型和字段存储定义 中,按需远程触发的功能被移除,详情参见 相关变更记录

请参考模块 Devel Entity Updates