创建自定义内容
目标受众
本文件主要面向具有面向对象 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;
}
?>
需要注意:
“为对象类型提供基本字段定义”。
- 它是 FieldableEntityInterface 的一个公共静态方法。
“用于定义实体字段的类”。
- 提供我们所需的所有方法,例如 创建 字段、添加约束 等。
完整实体
所以整体来看,我们的代码如下:
<?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。