Drupal 8 APIs

原文地址:https://www.drupal.org/developing/api/8

最后更新时间:2014年1月27日(官方文档)

Drupal 8 介绍了许多新APIs和保留了Drupal7/6的部分API。本节主要讲述下Drupal8的APIs相关细节。

在深入的文档之前,下面你可能需要阅读的背景和前提条件(链接外部),这些在很多的开发者文档中它解释了一些关于概念和术语等知识。

 

DRUPAL8数据库API

1、Drupal8数据库api概要

2、Drupal8数据库api - 实例化连接对象

3、DRUPAL8数据库API - 数据库配置

 

DRUPAL8数据库API - EXPRESSIONS

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8shu-ju-ku-api-guan-yu-drupal8dong-tai-cha-xun-de-jie-shao

原文地址:https://www.drupal.org/docs/8/api/form-api/configformbase-with-simple-configuration-api

Drupal8查询语句创建器支持在字段中使用expressions, 比如“所有的名字字段的数量”。请注意,许多表达式可以使用SQL函数,并不是所有SQL函数都在所有数据库中都是标准化的。只有模块开发人员才能确保只使用跨数据库兼容的表达式。
Drupal8的数据库层不提供SQL函数的跨数据库抽象。对于支持的数据库引擎的可移植性,各位Drupal开发者应该只使用已知的ANSI标准的一部分,并支持Drupal支持的所有数据库。下面是一个仍然不完整的列表。这里推荐使用的表单,因为其他语法变体可能对所有数据库都不起作用。
Drupal开发者请注意:数据库层没有白名单操作符,因此您可以通过非标准函数,如REPLACE(),它将用于支持语法的数据库。

 

DRUPAL8数据库API - 关于DRUPAL8动态查询的介绍

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8shu-ju-ku-api-guan-yu-drupal8dong-tai-cha-xun-de-jie-shao

 

 

1、关于Drupal8动态查询的介绍

动态查询指的是由Drupal8动态生成的查询,而不是作为显式查询字符串提供的查询。
所有的插入、更新、删除、合并语句都必需是动态查询,选择语句可以是动态查询或者静态查询,隐藏,动态查询通常关联动态的选择查询。
注意:在90%的选择查询例子中,你科选择静态查询,如果从性能优先的角度考虑,你应该使用query()而不是select();
如果查询语句是变化的或者允许被修改,才使用动态语句
所有动态生成的查询都是使用查询对象构造的,查询对象是从适当的连接对象请求的。
与静态查询一样,在绝大多数情况下,可以使用程序包装器来请求对象。然而,查询的后续指令采用查询对象上调用的方法的形式。
使用select()方法启动动态选择查询,如下所示:
$query = $connection->select('mytable', 'mt', $options);
在这种情况下,mytable是查询的基本表;也就是说,在from语句之后的第一个表。注意,它不应该有括号围绕它。查询生成器将自动处理。第二个参数是表的别名。如果未指定,则使用表的名称。$options数组是可选的,与静态查询的$options数组相同。
$connection->select()调用返回的值是类型选择的对象。因此,在此调用之后$query变量中的值类型是类型选择的对象。这个对象有一个完整的方法列表,比如fields(), joins()和 group(),它们可以被调用来进一步定义查询。
动态选择查询可以非常简单或非常复杂。下面我们将介绍组成一个简单查询的各个部分,在下面的页面中,我们将看到更先进的技术,如联接。

2、大局

这里是用户表的一个相对简单的查询。

假设我们想要创建一个动态查询,它大致相当于下面的静态查询:
$result = $connection->query("SELECT uid, name, status, created, access FROM {users} u WHERE uid <> 0 LIMIT 50 OFFSET 0");
动态等值开始如下:
// Create an object of type Select
$query = $connection->select('users', 'u');
 
// Add extra detail to this query object: a condition, fields and a range
$query->condition('u.uid', 0, '<>');
$query->fields('u', ['uid', 'name', 'status', 'created', 'access']);
$query->range(0, 50);
这通常是用用于在$query 对象同时调用多个方法的速记语法编写的。因此,上面的代码通常会写成如下:
// Create an object of type Select
$query = $connection->select('users', 'u');
 
// Add extra detail to this query object: a condition, fields and a range
$query->condition('u.uid', 0, '<>')
->fields('u', ['uid', 'name', 'status', 'created', 'access'])
->range(0, 50);
事实上,通过链式调用 $connection->select() ,代码可以并且通常被简化为一步,通过调用结果对象的方法调用。如下:
// Create an object of type Select and directly 
// add extra detail to this query object: a condition, fields and a range
$query = $connection->select('users', 'u')
->condition('u.uid', 0, '<>')
->fields('u', ['uid', 'name', 'status', 'created', 'access'])
->range(0, 50);
它是用户管理页面使用的查询的简化形式,可供进一步研究参考。

3、执行查询语句

一旦查询语句被建立,调用execute()方法可以编译和运行查询。
$result = $query->execute();
execute()方法可以返回一个结果集或者语句对象,这个语句对象与$connection->query()所返回的结果完全相同,并且可以以完全相同的方式迭代或获取:

$result = $query->execute();
foreach ($result as $record) {
// Do something with each $record
}
注意:当使用下列方法获取多列、动态查询的时候要非常小心。
fetchfield()
fetchallkeyed()
fetchallassoc
fetchcol()

4、Debuging

若要检查查询对象在其生命周期中某个特定点使用的SQL查询,请打印查询对象。若要检查参数,请查看由arguments()方法返回的数组:
echo $query;
print_r($query->__toString());
print_r($query->arguments());

DRUPAL8数据库API - 实例化连接对象

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8shu-ju-ku-api-shi-li-hua-lian-jie-dui-xiang.htm​

Drupal8数据库api - 实例化连接对象

Drupal8数据库交互应该通过连接对象来完成。实例化连接对象的最佳方法是通过服务容器。

这将导致连接对象被配置为连接到settings.php中的数据库配置中定义的默认主数据库。

在某些情况下,连接对象可能已经作为当前类上的成员可用;例如,许多插件和服务将连接对象作为成员。

使用不同的连接

如果您的站点使用多个数据库,则在默认数据库之外运行查询,使用Database::getConnection()。例如:

$connection = \Drupal\Core\Database\Database::getConnection('other_database');

以上的语句将提供一个对在settings.php中建立的数据库的连接,如下

$databases['other_database']['default']

DRUPAL8数据库API - 数据库配置

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8shu-ju-ku-api-shu-ju-ku-pei-zhi.htm​

DRUPAL8数据库API - 数据库配置
Drupal8 定义数据库连接的主要方法是通过settings.pgp中的$database数组。顾名思义,$databases允许定义多个数据库连接。它还支持多个目标的定义。
connection key(连接密钥)
$databases['default']
connection key(连接密钥)是给定数据库连接的唯一标识符。对于一个确定的站点,connection key(连接密钥)必须是唯一的,并且必须始终存在“默认”的连接,这将是主要的Drupal8数据库。在大多数站点上,它将是唯一定义的连接。
Target(目标)
$databases['default']['default']
一个确定的connection key (连接密钥)必须有一个或者多个目标。目标是可以使用的数据库,如果可用的话。必须为每个connection key(连接密钥)定义一个“缺省”目标。如果未定义请求的目标,系统将悄然退回到“默认”。
$databases 语法
$databases数组是至少三个级别的嵌套数组。第一级定义connection key(连接密钥)。第二个定义数据库目标。每个目标的值是该键/目标的连接信息。一些例子应该更清楚。
$databases['default']['default'] = array(
'driver' => 'mysql',
'database' => 'drupaldb',
'username' => 'username',
'password' => 'secret',
'host' => 'localhost',
);
上面的$databases数组定义了单个连接密钥(“default”),带有单个目标(“default”)。该连接使用名为'drupaldb'的本地主机MySQL数据库(“驱动程序”密钥),用户名为“用户名”和“秘密”密码。上面的示例是单个SQLServer Drupal安装的典型案例,对于绝大多数站点来说都是足够的.对于主/副本配置,我们将定义如下:
$databases['default']['default'] = array(
'driver' => 'mysql',
'database' => 'drupaldb1',
'username' => 'username',
'password' => 'secret',
'host' => 'dbserver1',
);
$databases['default']['replica'][] = array(
'driver' => 'mysql',
'database' => 'drupaldb2',
'username' => 'username',
'password' => 'secret',
'host' => 'dbserver2',
);
$databases['default']['replica'][] = array(
'driver' => 'mysql',
'database' => 'drupaldb3',
'username' => 'username',
'password' => 'secret',
'host' => 'dbserver3',
);
这个定义提供了一个“default”服务器和两个“replica”服务器。注意,“replica”键是一个数组。如果任何目标被定义为连接信息的数组,则对于每个页面请求,将针对该目标随机选择一个定义的服务器。也就是说,在一个页面请求中,所有副本查询将被发送到DbServ2,而下一个查询将全部发送到DbServ3。
$databases['default']['default'] = array(
'driver' => 'mysql',
'database' => 'drupaldb1',
'username' => 'username',
'password' => 'secret',
'host' => 'dbserver1',
);
$databases['extra']['default'] = array(
'driver' => 'sqlite',
'database' => 'files/extradb.sqlite',
);
此配置定义了一个主Drupal数据库和一个使用SQLite标记为“extra”的附加数据库。注意SQLite连接信息的结构不同于MySQL。每个驱动程序可能有不同的配置,这取决于什么是合适的。

请记住,不管您定义了多少连接,Drupal8在实际使用之前不会打开与该数据库的连接。

DRUPAL8数据库API - 静态查询

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8shu-ju-ku-api-jing-tai-cha-xun.htm

 

Drupal8中最常见的选择查询是使用连接对象或db_query()或者函数的query()方法进行静态查询。静态查询几乎逐字传递给数据库。
$connection = \Drupal::database();

$query = $connection->query("SELECT id, example FROM {mytable}");

$result = $query->fetchAll();
或者
$query = db_query("SELECT id, example FROM {mytable}");

$records = $query->fetchAll();

foreach ($records as $record) {
// Do something.

}

只有非常简单的SELECT查询才使用静态查询机制。
如果你有更复杂的查询,都用改是用动态查询。
不要使用这个函数进行插入、更新、删除语句,这些语句应该使用db_insert, db_update, db_delete

DRUPAL8数据库API概要

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8shu-ju-ku-apigai-yao.htm​
 

1、Drupal8数据库api概要

Drupal8的数据库API提供标准的、与底层无关的抽象层来访问数据库。不无需直接调用数据库除非你在开发核心的API

API的设计是为了尽可能多地保存SQL的语法和能力,包括:

1)更方便地支持多数据库服务器

2)允许开发者实现更复杂的功能,比如事务

3)为动态构建查询提供结构化接口

4)加强安全检查和其他良好习惯;

为模块提供一个干净的接口,用于截取和修改站点的查询

Drupal8主要数据库API文档是直接从代码中的注释导出的。该手册部分通过为希望与数据库系统交互的模块作者提供教程,并从管理员的角度对系统进行概述,从而扩充了API文档。

Drupal8数据库API是用面向对象的设计概念构建的,因此该文档至少部分地熟悉这些概念。通用操作也有可供使用的程序样式,但这些程序样式被弃用。建议使用连接对象进行数据库交互。

请注意,数据库API可能并不总是与数据交互的最佳选择。Drupal 8中的API使用通常是情境的,例如使用节点CRUD操作的节点API、实体创建的实体API等。请查看API文档以确定哪种API最适合您的需要。

注意:这本手册可能无法涵盖API的每一个特性。

Drupal 8 中的API迁移

原文链接:Migrate API in Drupal 8

Drupal 8 中的API迁移

注:截至2013年12月, 迁移API还在编写中,尚未完成。 本文档将大致介绍一下API,但期望在drupal 8发布前进行更新。

概述

迁移API提供了把数据从一个地方迁移到另一个地方(通常,导入整个Drupal实体[entities]中)的服务。 迁移模块实现了通用的框架,然而“Drupal迁移”是建立在从Drupal 6、Drupal 7、Drupal 8 一个升级路径基础之上。

理解迁移的最简单的方法是首先导入:源插件(source plugin)提供了行。 每一行是交给一系列进程插件(process plugins),最后直到插件保存到目的地(迁移地点)。

对于每一行,源、目的地都拥有属性和属性值。 例如,当导入节点, sticky是一个属性,可能值为0和1。 有一些属性是标识符,例如用户或者零售商的uid,产品进口的SKU。

标识符使我们能够跟踪变化等等。 一旦目的地插件保存,行和目的地标识符值就是可知的,我们保存源标识符的值,目的地标识符和行表的值就会存在在一个标识符(id)地图。 源标识符和行标(row hash)表允许对连续性迁移进行跟踪变化。 源标识符和目的地标识符允许查找值基于之前的迁移——如果源用户的uid值为44,到目的地之后它的uid值变为56,那么源节点uid 44可以查找到 目的地uid 为56的数值。

Highwater marks提供另一种跟踪变化的方法。 这就要求源(highwater 属性)告诉我们什么时候源记录的最后变化,如节点的change属性。 如果有一个highwater属性我们可以导入的改变了顺序,并保存了最后一次成功导入的时间戳记录。

所有的配置保存在整个迁移配置中。

配置通常包含四个关键部分:id、源、过程和迁移地。 第一个是一个简单的字符串,迁移的标识符。 其余部分我们将在各自的子页面中进行讨论。

这儿有一个关于实体迁移 的教程。

 

动态迁移

原文链接:Dynamic migrations (load plugins)

动态迁移(加载插件

当编写一个网站特定的迁移,目的地属性值是已知的。然而,当写一些更通用的,有可能迁移配置实体也需要写,尽管目的地属性值是不清楚的。 例如,当migrate_drupal核心模块不知道D6或D7节点。 所以它需要一些机制在php中来创建这些迁移而不是静态的YAML。
这种机制是加载插件。 当加载d6_cck_field_values:article :迁移、存储控制器认为这是一个动态的迁移d6_cck_field_values:article :基地迁移。它加载迁移,然后寻找一个加载(load)插件加载文章(article)。在这种情况下加载插件drupal_entity 配置流程复制源插件的基础领域的迁移。反过来,这些字段填写基于Drupal 6字段实例表和当前节点类型。 这些都是这是通过添加负载插件来实现:

load:
  plugin: drupal_entity
  bundle_migration: d6_node_type
然后指定d6_cck_field_values:*作为迁移到加载

执行一个Drupal 8迁移

先决条件: •友好的命令行( 没有用户界面 ) •PHP 5.4(检查也使用命令行) •git •Drush 7. x(dev)。 •Drupal 6网站(不推荐在生产运行迁移数据库) •最新版本的Drupal 8 •使模块迁移Drupal( drush en migrate_drupal) •确保Drush可以编写config目录。

没有UI,只有Drush

唯一的方法将数据迁移到Drupal8,使用Drupal迁移模块Drush 。 Drush支持的版本是Drupal8 和drupal7.x。 使用Drush迁移你需要最新的版本的drupal,所以 建议通过composer安装Drush 。 有一个UI创建( 迁移升级 ),但它仍然未完成。 这将是一个普通发布版模块,直到Drupal核心包括才算完成。

清单

为了执行迁移,您首先需要创建一个文件(称为manifest)迁移模块操作。 这里要记住的是当创建清单时: •它必须在Drupal的根目录。 •它只是一个文本文件。 •这个名称并不重要,但是类似于manifest.yml名称。 •它包含了各种执行迁移。 •对于所有你想要执行的迁移,您需要包括所有的依赖项的清单。 (各种迁移的依赖性在各自迁移可以找到配置文件,它位于core/modules/migrate_drupal/config/install/)。 •一个完整的清单可以找到所有D6 - > D8迁移 https://drupal.org/node/2221779 •迁移的顺序并不重要,因为迁移模块将重新排序。 •您可以包括评论,通常以#字符开头。 下面是一个示例运行D6用户迁移:

# A D6 user migration, with dependencies.
- d6_filter_format
- d6_user
- d6_user_picture_entity_display
- d6_user_picture_entity_form_display
- d6_user_picture_field
- d6_user_picture_field_instance
- d6_user_role
可以想象,这也将运行其他迁移上,而不仅仅是用户。

执行迁移

drush命令执行迁移:

drush migrate-manifest <manifest-name> --legacy-db-url=mysql://<username>:<password>@<host>/<db_name>
< manifest-name >是清单文件的名称创建的迁移。 路径不是必需的,但是扩展(如果有的话)。 ——db-url是一个Drupal 6风格数据库URL。 例如: drush migrate-manifest D6Manifest-User。 yml——legacy-db-url = mysql:/ /根:root@127.0.0.1 / d6注意,这个命令不同于D7 contrib迁移模块。 命令等drush migrate-import在Drupal 8不能正常运行。 执行命令时,您将会看到一个列表列出的迁移,他们正在处理,如下所示:
Running d6_filter_format
Running d6_user_role
Running d6_user_picture_field
Running d6_user_picture_field_instance
Running d6_user_picture_entity_display
Running d6_user_picture_entity_form_display
Running d6_user

Drupal 6 - > 8迁移的已知问题

Drupal 6 - > 8迁移的已知问题

下面列出的各种项目,可能需要特别注意当从Drupal 6迁移到Drupal 8。 节点类型 默认配置在D6在Drupal创建story和page内容类型但drupal8默认类型是article和basic page(有机器名为“页面”就像在D6)。 迁移将重现story类型但重用page类型。 从D6迁移的开发人员可能希望删除文章类型。#2236289: Migrating "story" nodes from D6 creates new content type in D8为进一步的细节。

URL别名

当url别名的语言迁移不启用新Drupal 8网站的别名将直到你启用新Drupal 8网站上的语言。

模块和主题

新的模块和主题需要启用,管理主题需要设置(如果有的话)。

配置文件类别

如果你在drupal6使用个人信息模块分组配置文件,您将需要在迁移之后手动重新创建分组。

概要文件字段(列表中选择)

在D8中的“允许值”设置的字段将所有所选用户的组合,在d6当前的允许值,不不仅仅是D6的允许值。

日期的格式

只有默认,短日期、中日期和长日期格式迁移。 所有其他格式默认格式迁移后需要重新配置。

谁是在线块

用户活动设置没有被迁移。 这需要手动编辑相关视图中的过滤器/访问。

视图

目前没有计划迁移自定义视图。 您将需要重新创建任何视图,当你的网站迁移之后(节点类型、字段等)。

聚合器

Drupal 8不再有聚合器的概念的,因此他们将不会从复D6/D7迁移到D8

迁移目的地

原文链接

迁移目的地

目的地插件必须有一个属性值,比较典型的是实体entity:entity_type.例如:

destination:
  plugin: entity:block

另一个典型的例子是url_alias插件,迁移URL别名。config配置 插件允许创建最初的配置元素:
destination:
  plugin: config
  config_name: book.settings

config_name:book.settings
参考子页面关于更多的目的地插件。

Drupal 8 的实体 API

最近一次更新时间 October 13, 2016 - 16:37

原文链接 https://www.drupal.org/docs/8/api/entity-api/introduction-to-entity-api-in-drupal-8

背景

在Drupal 7开发周期的晚期才引进了Entity的概念。非核心的entity.module对API进行的扩展,增加了保存和删除entities等改进。

这些改进中的大部分都已经包含到了Drupal 8中。Entity Validation现在有自己单独的API(例如,可以用于验证不是通过Form而是通过REST保存的Entity)。

THE Drupal 8 entity system

Entities就是带方法的特殊类

  • 普通方法 | $entity->id()
  • Entity类的特定方法 | $node->getTitle()

两种类型都在interfaces里面中有定义和文档。

Handlers

Handlers支持Eneities

Storage handler - 支持对entities进行loading,saving和deleting,还包含了对revisions,translations和可配置的fields的支持。另外还有支持access control, viewing, listing和forms的其他handlers。

两种类型

Drupal 8核心里面的Entity类来自于两种类型。

Configuration Entity 配置系统中使用Configuration Entity.支持翻译,并且能够为安装提供自定义的缺省值。

Content Entity 由可配置的和基本fields组成,可以有修订版本并且支持翻译。

本页面尚未成型,Drupal 8中的Entity API还未最终完成,文档也在更新中。 待完成/计划中:

- Handlers

- - Storage

- - Access

- - Forms

- - Views Builder

- - List

- Bundles

- Configuration entities -

Content entities

- - Revisions

- - Translations

- - Fields

- UUID support

- Entity query

- Routing

Drupal 8 Entity API issues being worked on:

2095603: [meta] Complete Entity Field API

1540656: [META] Entity Serialization API for web services (e.g. content staging)

_#1234567: Entity Forms API

实体 API 的使用 (翻译中)

原文地址: https://www.drupal.org/node/2124403

原文版本: May 28, 2014.

 

This covers the generic entity API, configuration entity and content entity specific API's will be covered in specific chapters. TODO: Link once created.

Check

 

<?php
// Make sure that an object is an entity.
if ($object instanceof \Drupal\Core\Entity\EntityInterface) {
}
// Make sure it's a content entity.
if ($entity instanceof \Drupal\Core\Entity\ContentEntityInterface) {
}
// Get the entity type.
$entity->entityTypeId();
// Make sure it's a node.
if ($entity instanceof \Drupal\node\NodeInterface) {
}
// Using entityType() works better when the needed entity type is dynamic.
$needed_type = 'node';
if (
$entity->entityTypeId() == $needed_type) {
}
?>

Get information from an entity/ Entity methods

A number of generic methods are available to get information from an entity, like the ID, bundle, revision ID and so on. See the documentation on the EntityInterface for details.

 

<?php
// Get the ID.
$entity->id();
// Get the bundle.
$entity->bundle();
// Check if the entity is new.
$entity->isNew();
// Get the label of an entity. Replacement for entity_label().
$entity->label().
// Get the URI for an entity.
// @todo: This might still change with the new URI template API.
$entity->uri();
// Create a duplicate that can be saved as a new entity.
$duplicate = $entity->createDuplicate();
?>

Create

 

<?php
// Use the procedural wrapper.
$node = entity_create('node', array(
 
'title' => 'My node',
 
'body' => 'The body content. This just works like this due to the new Entity Field
          API. It will be assigned as the value of the first field item in the
          default language.'
,
));
// Or you can use the static create() method if you know
// the entity class::
$node = Node::create(array('title' => 'The node title'));
// Use the entity manager.
$node = \Drupal::entityManager()->getStorageController('node')->create(array('title' => 'Another node'));
?>

 

<?php

?>

Load

 

<?php
// Use the static method
$node = Node::load(1);
// Dynamic entity type, entity_load() now loads a single entity, the 7.x entity_load() has been renamed to entity_load_multiple().
$entity = entity_load($entity_type, $id);
// Using the storage controller.
$entity = \Drupal::entityManager()->getStorageController($entity_type)->load(1);
// Load multiple entities, also exists as entity_load_multiple().
$entity = \Drupal::entityManager()->getStorageController($entity_type)->loadMultiple(array(1, 2, 3));
?>

Save

 

<?php
// To save an entity.
$entity->save();
?>

That works for both new and existing entities, the entity itself keeps track whether it's new or not. By default, for content entities, that depends on whether it has an ID or not. To save an entity that has an entity as a new entity (e.g, when importing something), the isNew flag can be enforced.

<?php
// The following will attempt to insert a new node with the ID 5, this will fail if that node already exists.
$node->nid->value = 5;
$node->enforceIsNew(TRUE);
$node->save()
?>

Delete

 

<?php
// Delete a single entity.
$entity->delete();
// Delete multiple entities at once.
\Drupal::entityManager()->getStorageController($entity_type)->delete(array($id1 => $entity1, $id2 => $entity2));
?>

Access control

The access()method can be used to check who can do what with an entity. The method supports different operations, the standard operations are view, update, deleteand create, create is a somewhat special, see below.

Access checks are forwarded to the access controller. (TODO: Add link)

 

<?php
// Check view access of an entity.
// This defaults to check access for the currently logged in user.
if ($entity->access('view')) {
}
// Check if a given user can delete an entity.
if ($entity->access('delete', $account)) {
}
?>

When checking create access, there is usually no entity yet. Creating one just to check if someone would be able to create it, is a costly operation, therefor, create access should for those should be checked directly on the access controller.

<?php
\Drupal::entityManager()->getAccessController('node')->createAccess('article');
?>

If there is already an entity, $entity-&gt;access('create')works too, which just forwards to the createAccess() method, the same way other operations forward to the access() method on the access controller.

梱束 (翻译中)

原文地址: https://www.drupal.org/node/2143499

原文未成

配置型实体 (翻译中)

原文地址: https://www.drupal.org/node/2143501

原文版本: May 9, 2014.

Configuration entities using the new entity API in Drupal 8 in order to keep configuration into the DB.
The difference between the config entities to content entites:

  1. Configurable entity can be exported using the new CMI system.
  2. Configurable entity don't have field due to the way they stored in the DB
  3. Configurable entity defined a schema file unlike content entities which defined by hook_schema()

内容型实体 (翻译中)

原文地址: https://www.drupal.org/node/2143503

原文版本:  June 26, 2014. 

(原文未成)

 

(Stub)

Content entities examples

  • node
  • comment
  • user

控制器/处理程序*(翻译中)

原文地址: https://www.drupal.org/node/2143497

原文版本: November 25, 2013. 

(原文未成)

 

 

* Final name not yet defined, see #1976158: Rename entity storage/list/form/render "controllers" to handlers

While entities represent a piece of data, controllers are responsible for acting on and with them. The basic concept was introduced in Drupal 7, where the controller provided the API to load entities. That class was then extended in the entity.module to provide full CRUD support and other types of controllers were created.

Those controllers have been included and extend in Drupal 8. Controllers of an entity can be accessed through the entity_manager service:

Storage

The main controller is storage, which has to implement EntityStorageControllerInterface, with the following default controllers:

  • \Drupal\Core\Entity\FieldableDatabaseStorageController
    The main storage controller for content entities, supports revisions, translations and configurable fields.
  • \Drupal\Core\Config\Entity\ConfigStorageController
    The main storage controller for configuration entities.
  • \Drupal\Core\Entity\FieldableDatabaseStorageController
    Alternative storage controller for simple non-content entities. Currently only used temporarly for menu link entities, might not remain in Core

Example usage:

<?php
$storage
= \Drupal::entityManager()->getStorageController('node');
$storage->load(1);
$storage->loadMultiple(array(1, 2, 3));
// Equaivalent to $node->save().
$storage->save($node);
$new_node = $storage->create(array('title' => 'My awesome node'));
?>

实体注解的结构 (翻译中)

原文地址: https://www.drupal.org/node/2207559

原文版本: August 25, 2014.

 

Entity types, both configuration and content entity are defined using annotation in the entity class.

The Example entity is defined in Drupal\example\Entity\Example.php (assumes the module name is "example"):

<?php
/**
 * @file
 * Definition of Drupal\example\Entity\Example.
 */
namespace Drupal\example\Entity;
use
Drupal\Core\Config\Entity\ConfigEntityBase;
/**
 * Defines an example configuration entity.
 *
 * @ConfigEntityType(
 *   id = "example",
 *   label = @Translation("Example"),
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *     "uuid" = "uuid"
 *   }
 * )
 */
class Example extends ConfigEntityBase {
 
// ...
}
?>

Configuration entities use "@ConfigEntityType" and extend the Drupal\Core\Config\Entity\ConfigEntityBase class. Content entities use "@ContentEntityType" and extend the Drupal\Core\Config\Entity\ContentEntityBase class.

The following properties can be used in entity type annotations:

id (required)
Unique identifier. Use the module name, optionally with an additional _suffix.
config_prefix (configuration only)
Additional configuration identifier. Typically the suffix of the ‘id’ property. Example: id = “node_type”, config_prefix = “type”. The resulting configuration file will have module name as prefix. So {module}.{config_prefix}.{id key value} would be node.type.article
label
Human readable entity name. Example: @Translation("Breakpoint")
bundle_label
Human readable bundle name. Example: @Translation("Content type")
controllers
Array of entity controller classes, keyed by controller type.

storage: The class that loads and saves the object.

form: Array of entity forms classes keyed by their operation (such as 'create', 'edit', or 'delete'). One class can be used for multiple entity operations. This routes can be accessed in *routing.yml via eg. _entity_form: 'foobar.create'

list: The class that provides listings of the entity.

render: The class that renders the entity.

access: The class that is used for access checks. Defaults to \Drupal\Core\Entity\EntityAccessController.

view_builder: The class that provides the entity view builder.

translation: The class that is used for entity translation.

base_table (content only)
The name of the entity's base table. Example: “node”
data_table (content only)
The name of the entity's data table. Example: “node_field_data”
revision_table (content only)
the name of the entity's revision table. Example: “node_revision”
revision_data_table (content only)
The name of the entity's revision data table. Example: “node_field_revision”
admin_permission
The permission required to administer this entity. Example: admin_permission = "administer blocks" Entities with more complex permissions can set their own access controller.
admin_permission
Whether fields can be attached to the entity. Defaults to FALSE.
field_cache
Whether the persistent cache of field data should be used. Defaults to TRUE.
uri_callback
A function (defined in *.module) or class and method that provides the entity URI.
translatable
Whether the entity has multilingual support. Defaults to FALSE.
fieldable
Boolean value, if true additional fields can be added to the entity via gui (ie. via manage fields tab).
render_cache
Whether the rendered output of the entity should be cached. Defaults to TRUE.
entity_keys
Names of object properties that contain certain entity information. Example: $entity->title contains the entity label. Keys: id, label, uuid. Optional keys: bundle, revision.
bundle_entity_type
The name of the entity type which provides bundles. Example: bundle_entity_type = "node_type"
permission_granularity
TODO: Needs description. Allowed values: "entity_type" (default), "bundle" or FALSE.
links
Entity URL definitions, referencing to routes. 'canonical' is the default route for. TODO: Needs description.

Upgrading Code Snippets Module to Drupal 8: Creating a Custom Field (翻译中)

原文地址: https://www.drupal.org/node/2128865

原文版本: December 13, 2013.

(原文未成)

 

This tutorial was originally published on Web Wash. However, Berdir asked if I could put the tutorial here, so here it is.

The module in Drupal 7 allows you to store code examples/snippets in a field. It ships with a custom field called "Snippets field" and it renders three form elements, description, source code and syntax highlighting mode (what programming language).

But now it's time to upgrade the module to Drupal 8.

In this tutorial, I'll show you how I created a "basic" custom field in Drupal 8. I won't go into detail about PSR–0, annotations or plugins or this tutorial will be huge.

Instead, I'll add links to other websites that explain the concept further.

That being said, if you're looking for detailed documentation on the Field API in Drupal 8, check out the following series:

In Drupal 8, fields are not implemented using hooks like they are in Drupal 7. Instead, they are created using Drupal 8's new Plugin API. This means that instead of implementing hooks, we define a class for a widget, formatter and field item. Most Drupal 7 field hooks like hook_field_schema, hook_field_is_empty and more; are now methods in classes.

Step 1: Implement Field Item

The first bit of work we need to do is define a field item class called SnippetsItem that extends the ConfigFieldItemBase class.

1. In Drupal 8 classes are loaded using PSR-0.

So, to define the SnippetsItem class, we need to create a SnippetsItem.php file and place it in "module"/lib/Drupal/snippets/Plugin/Field/FieldType/SnippetsItem.php

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\snippets\Plugin\Field\FieldType\SnippetsItem.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">snippets</span><span style="color: #007700">\</span><span style="color: #0000BB">Plugin</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldType</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">ConfigFieldItemBase</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldInterface</span><span style="color: #007700">;<br></span><span style="color: #0000BB">?&gt;</span></span>

Then in the file we add a namespace Drupal\snippets\Plugin\Field\FieldType and two use statements: Drupal\Core\Field\ConfigFieldItemBase and Drupal\field\FieldInterface.

2. Now we need to define the actual field details like the field id, label, default widget and formatter etc.. This the equivalent of implementing hook_field_info in Drupal 7.

In Drupal 8 a lot, if not all, of the info hooks have been replaced by annotations.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* Plugin implementation of the 'snippets' field type.<br>&nbsp;*<br>&nbsp;* @FieldType(<br>&nbsp;*&nbsp;&nbsp; id = "snippets_code",<br>&nbsp;*&nbsp;&nbsp; label = @Translation("Snippets field"),<br>&nbsp;*&nbsp;&nbsp; description = @Translation("This field stores code snippets in the database."),<br>&nbsp;*&nbsp;&nbsp; default_widget = "snippets_default",<br>&nbsp;*&nbsp;&nbsp; default_formatter = "snippets_default"<br>&nbsp;* )<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">SnippetsItem </span><span style="color: #007700">extends </span><span style="color: #0000BB">ConfigFieldItemBase </span><span style="color: #007700">{ }<br></span><span style="color: #0000BB">?&gt;</span></span>

So instead of implementing hook_field_info, we define the field as an annotation inside of a comment above the class.

The annotation attributes are quite self-explanatory. Just make sure that the default_widget and default_formatter reference the widget and formatter annotation ID and not the class.

If you want to learn more about annotations, check out the Annotations-based plugins documentation page on drupal.org.

3. Now that we have our field item class, we need to define a few methods. The first one we'll look at is schema().

In Drupal 7, when you create a custom field you define its schema using hook_field_schema. In Drupal 8, we define the schema by adding a schema() method to the SnippetsItem class.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* {@inheritdoc}<br>&nbsp;*/<br></span><span style="color: #007700">public static function </span><span style="color: #0000BB">schema</span><span style="color: #007700">(</span><span style="color: #0000BB">FieldInterface $field</span><span style="color: #007700">) {<br>&nbsp; return array(<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'columns' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'source_description' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'varchar'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'length' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">256</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'not null' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'source_code' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'text'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'size' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'big'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'not null' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'source_lang' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'varchar'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'length' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">256</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'not null' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp; ),<br>&nbsp; );<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>

4. Now we need to add the isEmpty() method and define what constitutes an empty field item. This method is the same as implementing hook_field_is_empty in Drupal 7.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* {@inheritdoc}<br>&nbsp;*/<br></span><span style="color: #007700">public function </span><span style="color: #0000BB">isEmpty</span><span style="color: #007700">() {<br>&nbsp; </span><span style="color: #0000BB">$value </span><span style="color: #007700">= </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">get</span><span style="color: #007700">(</span><span style="color: #DD0000">'source_code'</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">getValue</span><span style="color: #007700">();<br>&nbsp; return </span><span style="color: #0000BB">$value </span><span style="color: #007700">=== </span><span style="color: #0000BB">NULL </span><span style="color: #007700">|| </span><span style="color: #0000BB">$value </span><span style="color: #007700">=== </span><span style="color: #DD0000">''</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>

5. The final method we'll add to the class is the getPropertyDefinitions() method.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* {@inheritdoc}<br>&nbsp;*/<br></span><span style="color: #007700">static </span><span style="color: #0000BB">$propertyDefinitions</span><span style="color: #007700">;<br></span><span style="color: #FF8000">/**<br>&nbsp;* {@inheritdoc}<br>&nbsp;*/<br></span><span style="color: #007700">public function </span><span style="color: #0000BB">getPropertyDefinitions</span><span style="color: #007700">() {<br>&nbsp; if (!isset(static::</span><span style="color: #0000BB">$propertyDefinitions</span><span style="color: #007700">)) {<br>&nbsp;&nbsp;&nbsp; static::</span><span style="color: #0000BB">$propertyDefinitions</span><span style="color: #007700">[</span><span style="color: #DD0000">'source_description'</span><span style="color: #007700">] = </span><span style="color: #0000BB">DataDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'string'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Snippet description'</span><span style="color: #007700">));<br>&nbsp;&nbsp;&nbsp; static::</span><span style="color: #0000BB">$propertyDefinitions</span><span style="color: #007700">[</span><span style="color: #DD0000">'source_code'</span><span style="color: #007700">] = </span><span style="color: #0000BB">DataDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'string'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Snippet code'</span><span style="color: #007700">));<br>&nbsp;&nbsp;&nbsp; static::</span><span style="color: #0000BB">$propertyDefinitions</span><span style="color: #007700">[</span><span style="color: #DD0000">'source_lang'</span><span style="color: #007700">] = </span><span style="color: #0000BB">DataDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'string'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Snippet code language'</span><span style="color: #007700">));<br>&nbsp; }<br>&nbsp; return static::</span><span style="color: #0000BB">$propertyDefinitions</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>

This method is used to define the type of data that exists in the field values. The "Snippets field" has just three values: description, code and language. So I just added those values to the method as strings.

Go to the How Entity API implements Typed Data API documentation on drupal.org to learn more about this.

Click here to see the whole file.

Step 2: Implement Field Widget

Now that we've defined the field item, let's create the field widget. We need to create a class called SnippetsDefaultWidget that extends the WidgetBase class.

1. So create a SnippetsDefaultWidget.php file and add it to "module"/lib/Drupal/snippets/Plugin/Field/FieldWidget/SnippetsDefaultWidget.php.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\snippets\Plugin\Field\FieldWidget\SnippetsDefaultWidget.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">snippets</span><span style="color: #007700">\</span><span style="color: #0000BB">Plugin</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldWidget</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldItemListInterface</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">WidgetBase</span><span style="color: #007700">;<br></span><span style="color: #0000BB">?&gt;</span></span>

Make sure the file namespace is Drupal\snippets\Plugin\Field\FieldWidget and add the following two use statements: Drupal\Core\Field\FieldItemListInterface and Drupal\Core\Field\WidgetBase.

2. Next, we need to define the widget using an annotation. This is the equivalent of using hook_field_widget_info in Drupal 7.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* Plugin implementation of the 'snippets_default' widget.<br>&nbsp;*<br>&nbsp;* @FieldWidget(<br>&nbsp;*&nbsp;&nbsp; id = "snippets_default",<br>&nbsp;*&nbsp;&nbsp; label = @Translation("Snippets default"),<br>&nbsp;*&nbsp;&nbsp; field_types = {<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "snippets_code"<br>&nbsp;*&nbsp;&nbsp; }<br>&nbsp;* )<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">SnippetsDefaultWidget </span><span style="color: #007700">extends </span><span style="color: #0000BB">WidgetBase </span><span style="color: #007700">{ }<br></span><span style="color: #0000BB">?&gt;</span></span>

Just a heads up, make sure that the field_types attribute in the annotation references the field types using their ID. For this module, it is snippets_code because we added id = "snippets_code", to the @FieldType annotation.

3. And finally, we need to define the actual widget form. We do this by adding a formElement() method to the SnippetsDefaultWidget class. This method is the same as using hook_field_widget_form in Drupal 7.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* {@inheritdoc}<br>&nbsp;*/<br></span><span style="color: #007700">public function </span><span style="color: #0000BB">formElement</span><span style="color: #007700">(</span><span style="color: #0000BB">FieldItemListInterface $items</span><span style="color: #007700">, </span><span style="color: #0000BB">$delta</span><span style="color: #007700">, array </span><span style="color: #0000BB">$element</span><span style="color: #007700">, array &amp;</span><span style="color: #0000BB">$form</span><span style="color: #007700">, array &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br>&nbsp; </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'source_description'</span><span style="color: #007700">] = array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Description'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; isset(</span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">]-&gt;</span><span style="color: #0000BB">source_description</span><span style="color: #007700">) ? </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">]-&gt;</span><span style="color: #0000BB">source_description </span><span style="color: #007700">: </span><span style="color: #0000BB">NULL</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp; </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'source_code'</span><span style="color: #007700">] = array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Code'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textarea'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; isset(</span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">]-&gt;</span><span style="color: #0000BB">source_code</span><span style="color: #007700">) ? </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">]-&gt;</span><span style="color: #0000BB">source_code </span><span style="color: #007700">: </span><span style="color: #0000BB">NULL</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp; </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'source_lang'</span><span style="color: #007700">] = array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Source language'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; isset(</span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">]-&gt;</span><span style="color: #0000BB">source_lang</span><span style="color: #007700">) ? </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">]-&gt;</span><span style="color: #0000BB">source_lang </span><span style="color: #007700">: </span><span style="color: #0000BB">NULL</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br>&nbsp; return </span><span style="color: #0000BB">$element</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>

Click here to see the whole file.

Step 3: Implement Field Formatter

The final piece to the puzzle, is the field formatter, and we create it by defining a class called SnippetsDefaultFormatter that extends the FormatterBase class.

1. Create a SnippetsDefaultFormatter.php file and add it to "module"/lib/Drupal/snippets/Plugin/Field/FieldFormatter/SnippetsDefaultFormatter.php.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\snippets\Plugin\field\formatter\SnippetsDefaultFormatter.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">snippets</span><span style="color: #007700">\</span><span style="color: #0000BB">Plugin</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldFormatter</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FormatterBase</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldItemListInterface</span><span style="color: #007700">;<br></span><span style="color: #0000BB">?&gt;</span></span>

Make sure the file namespace is Drupal\snippets\Plugin\Field\FieldFormatter and add the following use statements: Drupal\Core\Field\FieldItemListInterface and Drupal\Core\Field\FormatterBase.

2. Next, we need to define the formatter as an annotation. The same as we did for the widget and field type, this is the equivalent of using hook_field_formatter_info.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* Plugin implementation of the 'snippets_default' formatter.<br>&nbsp;*<br>&nbsp;* @FieldFormatter(<br>&nbsp;*&nbsp;&nbsp; id = "snippets_default",<br>&nbsp;*&nbsp;&nbsp; label = @Translation("Snippets default"),<br>&nbsp;*&nbsp;&nbsp; field_types = {<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "snippets_code"<br>&nbsp;*&nbsp;&nbsp; }<br>&nbsp;* )<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">SnippetsDefaultFormatter </span><span style="color: #007700">extends </span><span style="color: #0000BB">FormatterBase </span><span style="color: #007700">{ }<br></span><span style="color: #0000BB">?&gt;</span></span>

3. Now the only thing left to do is add the viewElements() method and define the actual field formatter. Again, this method is the same as using hook_field_formatter_view in Drupal 7.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* {@inheritdoc}<br>&nbsp;*/<br></span><span style="color: #007700">public function </span><span style="color: #0000BB">viewElements</span><span style="color: #007700">(</span><span style="color: #0000BB">FieldItemListInterface $items</span><span style="color: #007700">) {<br>&nbsp; </span><span style="color: #0000BB">$elements </span><span style="color: #007700">= array();<br>&nbsp; foreach (</span><span style="color: #0000BB">$items </span><span style="color: #007700">as </span><span style="color: #0000BB">$delta </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$item</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Render output using snippets_default theme.<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$source </span><span style="color: #007700">= array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#theme' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'snippets_default'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#source_description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$item</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">source_description</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#source_code' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$item</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">source_code</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$elements</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">] = array(</span><span style="color: #DD0000">'#markup' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">drupal_render</span><span style="color: #007700">(</span><span style="color: #0000BB">$source</span><span style="color: #007700">));<br>&nbsp; }<br>&nbsp; return </span><span style="color: #0000BB">$elements</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>

One thing to take note of is that I'm using a custom template called snippets_default to render the snippets before it is displayed by the formatter.

The reason for this is I didn't want to put a lot of logic or HTML code in the viewElements() method.

Click here to see the whole file.

Conclusion

As stated earlier the biggest change in Drupal 8 is that fields are created using the Plugin API and not hooks. Once you understand that, the concept of creating a field is very similar to Drupal 7. A lot of the methods in Drupal 8 match the hooks in Drupal 7.

If you want to test out Code Snippets, download the 8.x-dev release and give it a go.

HAL: Serialization using the Hypertext Application Language (翻译中)

原文地址: https://www.drupal.org/documentation/modules/hal

原文版本: August 9, 2014. 

(原文未成)

The HAL module is included in Drupal core. When enabled, the HAL module serializes entities using HAL (Hypertext Application Language).

What is it?

For general information about the language, see the informal or formal (06) HAL specification.

Usage

After installation and configuration you can test drive your site providing json data with a HAL browser.

Dependencies

实体引用

原文地址: https://www.drupal.org/documentation/modules/entityreference

原文版本: August 30, 2013.

自 Drupal 8 以往, 实体引用 (Entity Reference) 模块成为核心模块, 它提供一个字段用以引用其他实体.

需要有关实体的更多信息, 请参见 实体 API 文档.

在 Drupal 8 中创建内容型实体类型 (翻译中)

原文地址: https://www.drupal.org/node/2192175

原文版本:  September 23, 2014.

 

This page provides an example of how to create a content entity type, with administration management pages, for Drupal 8.

Note: We try to improve the module as well as the documentation in sync with the development process of core. It is intended for usage with the latest dev version. You can also have a peek in core modules such as node and comment which make use of the ContentEntityType.

If you run into troubles while creating your own Content Entity be sure to check your watchdog log. Additionally inspect your apache log too. It will inform you about common mistakes such as missing use-statements for which Drupal 8 will only response with a white screen of death/WSOD (HTTP 500) in this case.

Set up module and admin menu entry

foo_bar/foo_bar.info.yml

name: Foo Bar<br>type: module<br>description: 'Provides FooBar entity.'<br>package: FooBar<br>core: 8.x<br>dependencies:<br>&nbsp; - entity


foo_bar/foo_bar.module

The hook_menu_link_defaults() function defines an administrative menu entry for managing your foo_bar entity.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains Drupal\foo_bar\foo_bar.module<br>&nbsp;*/<br>/**<br>&nbsp;* Implements hook_permission().<br>&nbsp;*/<br></span><span style="color: #007700">function </span><span style="color: #0000BB">foo_bar_permission</span><span style="color: #007700">() {<br>&nbsp; return array(<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'delete_foo_bar' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Delete entity content.'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'add_foo_bar' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Add entity content'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'view_foo_bar' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'View entity content'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'edit_foo_bar' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Edit entity content'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp; ),<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'admin_foo_bar' </span><span style="color: #007700">=&gt; array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Administer settings'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp; ),<br>&nbsp; );<br>}<br></span><span style="color: #FF8000">/**<br>&nbsp;* Implements hook_theme().<br>&nbsp;*/<br></span><span style="color: #007700">function </span><span style="color: #0000BB">bookmark_theme</span><span style="color: #007700">() {<br>&nbsp; </span><span style="color: #FF8000">/*<br>&nbsp;&nbsp;&nbsp; * You can implement this hook if you don't want an annoying watchdog message in your log.<br>&nbsp;&nbsp;&nbsp; * However, it isn't required to implement it.<br>&nbsp;&nbsp; */<br></span><span style="color: #007700">}<br></span><span style="color: #0000BB">?&gt;</span></span>


Routing

foo_bar/foo_bar.routing.yml

The routing.yml file defines the routes for the management pages: view, list, add, edit, delete and settings.

foo_bar.view:<br>&nbsp; path: '/foo-bar/{foo_bar}'<br>&nbsp; defaults:<br>&nbsp;&nbsp;&nbsp; _entity_view: 'foo_bar'<br>&nbsp;&nbsp;&nbsp; _title: 'Foo Bar Content'<br>&nbsp; requirements:<br>&nbsp;&nbsp;&nbsp; _permission: 'view_foo_bar'<br>foo_bar.list:<br>&nbsp; path: '/foo-bar/list'<br>&nbsp; defaults:<br>&nbsp;&nbsp;&nbsp; _entity_list: 'foo_bar'<br>&nbsp;&nbsp;&nbsp; _title: 'Foo Bar Content List'<br>&nbsp; requirements:<br>&nbsp;&nbsp;&nbsp; _permission: 'view_foo_bar'<br>foo_bar.add:<br>&nbsp; path: '/foo-bar/add'<br>&nbsp; defaults:<br>&nbsp;&nbsp;&nbsp; _entity_form: foo_bar.add<br>&nbsp;&nbsp;&nbsp; _title: 'Add foo bar Content'<br>&nbsp; requirements:<br>&nbsp;&nbsp;&nbsp; _permission: 'add_foo_bar'<br>foo_bar.edit:<br>&nbsp; path: '/foo-bar/{foo_bar}/edit'<br>&nbsp; defaults:<br>&nbsp;&nbsp;&nbsp; _entity_form: foo_bar.edit<br>&nbsp;&nbsp;&nbsp; _title: 'Edit foo bar content'<br>&nbsp; requirements:<br>&nbsp;&nbsp;&nbsp; _permission: 'edit_foo_bar'<br>foo_bar.delete:<br>&nbsp; path: '/foo-bar/{foo_bar}/delete'<br>&nbsp; defaults:<br>&nbsp;&nbsp;&nbsp; _entity_form: foo_bar.delete<br>&nbsp;&nbsp;&nbsp; _title: 'Delete foo bar Content '<br>&nbsp; requirements:<br>&nbsp;&nbsp;&nbsp; _permission: 'delete_foo_bar'<br>foo_bar.settings:<br>&nbsp; path: 'admin/structure/foo_bar_settings'<br>&nbsp; defaults:<br>&nbsp;&nbsp; _form: '\Drupal\foo_bar\Entity\Form\FooBarSettingsForm'<br>&nbsp;&nbsp; _title: 'FooBar Settings'<br>&nbsp; requirements:<br>&nbsp;&nbsp;&nbsp; _permission: 'admin_foo_bar'

foo_bar/foo_bar.links.action.yml

This makes the "Add" link appear on the List page.

foo_bar.add:<br>&nbsp; route_name: foo_bar.add<br>&nbsp; title: 'Add Foo Bar Content'<br>&nbsp; appears_on:<br>&nbsp;&nbsp;&nbsp; - foo_bar.list


foo_bar/foo_bar.links.task.yml

The "View/Edit/Delete" tabs will appear on the entity view page.
The "Settings" tab will appear on the entity settings page.

foo_bar.settings_tab:<br>&nbsp; route_name: foo_bar.settings<br>&nbsp; title: 'Settings'<br>&nbsp; base_route: foo_bar.settings<br>foo_bar.view:<br>&nbsp; route_name: foo_bar.view<br>&nbsp; base_route: foo_bar.view<br>&nbsp; title: 'View'<br>foo_bar.page_edit:<br>&nbsp; route_name: foo_bar.edit<br>&nbsp; base_route: foo_bar.view<br>&nbsp; title: Edit<br>foo_bar.delete_confirm:<br>&nbsp; route_name: foo_bar.delete<br>&nbsp; base_route: foo_bar.view<br>&nbsp; title: Delete<br>&nbsp; weight: 10


Entity type classes

foo_bar/src/FooBarInterface.php

All additional fields, which are not entity_keys must be defined with a getter and a setter in your content entity interface.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\foo_bar\FooBarInterface.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">EntityInterface</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">EntityTypeInterface</span><span style="color: #007700">;<br></span><span style="color: #FF8000">/**<br>&nbsp;* Provides an interface defining a Foo Bar entity.<br>&nbsp;*/<br></span><span style="color: #007700">interface </span><span style="color: #0000BB">FooBarInterface </span><span style="color: #007700">extends </span><span style="color: #0000BB">EntityInterface </span><span style="color: #007700">{<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Returns the identifier.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @return int<br>&nbsp;&nbsp; *&nbsp;&nbsp; The entity identifier.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">id</span><span style="color: #007700">();<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Returns the entity UUID (Universally Unique Identifier).<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * The UUID is guaranteed to be unique and can be used to identify an entity<br>&nbsp;&nbsp; * across multiple systems.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @return string<br>&nbsp;&nbsp; *&nbsp;&nbsp; The UUID of the entity.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">uuid</span><span style="color: #007700">();<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Return the Value of Foo Bar Field.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @return string<br>&nbsp;&nbsp; *&nbsp;&nbsp; The content of the field.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">getFooBarField</span><span style="color: #007700">();<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Defines the base fields of the entity type.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @param string $entity_type<br>&nbsp;&nbsp; *&nbsp;&nbsp; Name of the entity type<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @return \Drupal\Core\Field\FieldDefinitionInterface[]<br>&nbsp;&nbsp; *&nbsp;&nbsp; An array of entity field definitions, keyed by field name.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public static function </span><span style="color: #0000BB">baseFieldDefinitions</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityTypeInterface $entity_type</span><span style="color: #007700">);<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>


foo_bar/src/Entity/FooBar.php

This file defines the content entity class.

If you want to use the same form for "add" and "edit" then simply use "default" for both in the @ContentEntityType Annotation.

Note: hook_schema() was deprecated. It is only required to implement baseFieldDefinitions() for your database schema.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\foo_bar\Entity\FooBar.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">EntityStorageInterface</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Field</span><span style="color: #007700">\</span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">ContentEntityBase</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">\</span><span style="color: #0000BB">FooBarInterface</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">EntityTypeInterface</span><span style="color: #007700">;<br></span><span style="color: #FF8000">/**<br>&nbsp;* Defines the Foo Bar entity.<br>&nbsp;*<br>&nbsp;* @ContentEntityType(<br>&nbsp;*&nbsp;&nbsp; id = "foo_bar",<br>&nbsp;*&nbsp;&nbsp; label = @Translation("FooBar entity"),<br>&nbsp;*&nbsp;&nbsp; handlers = {<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "list_builder" = "Drupal\foo_bar\Entity\Controller\FooBarListBuilder",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "form" = {<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "add" = "Drupal\foo_bar\Entity\Form\FooBarFormController",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "edit" = "Drupal\foo_bar\Entity\Form\FooBarFormController",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "delete" = "Drupal\foo_bar\Entity\Form\FooBarDeleteForm"<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; },<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "translation" = "Drupal\content_translation\ContentTranslationController"<br>&nbsp;*&nbsp;&nbsp; },<br>&nbsp;*&nbsp;&nbsp; base_table = "foo_bar",<br>&nbsp;*&nbsp;&nbsp; admin_permission = "admin_foo_bar",<br>&nbsp;*&nbsp;&nbsp; fieldable = TRUE,<br>&nbsp;*&nbsp;&nbsp; translatable = TRUE,<br>&nbsp;*&nbsp;&nbsp; entity_keys = {<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "id" = "fbid",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "label" = "name",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "uuid" = "uuid"<br>&nbsp;*&nbsp;&nbsp; },<br>&nbsp;*&nbsp;&nbsp; links = {<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "edit-form" = "foo_bar.edit",<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp; "delete-form" = "foo_bar.delete"<br>&nbsp;*&nbsp;&nbsp; },<br>&nbsp;*&nbsp;&nbsp; field_ui_base_route = "foo_bar.settings",<br>&nbsp;* )<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">FooBar </span><span style="color: #007700">extends </span><span style="color: #0000BB">ContentEntityBase </span><span style="color: #007700">implements </span><span style="color: #0000BB">FooBarInterface </span><span style="color: #007700">{<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">id</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">get</span><span style="color: #007700">(</span><span style="color: #DD0000">'fbid'</span><span style="color: #007700">)-&gt;</span><span style="color: #0000BB">value</span><span style="color: #007700">;<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">getFooBarField</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">foo_bar_field</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">value</span><span style="color: #007700">;<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public static function </span><span style="color: #0000BB">preCreate</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityStorageInterface $storage_controller</span><span style="color: #007700">, array &amp;</span><span style="color: #0000BB">$values</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">preCreate</span><span style="color: #007700">(</span><span style="color: #0000BB">$storage_controller</span><span style="color: #007700">, </span><span style="color: #0000BB">$values</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$values </span><span style="color: #007700">+= array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'user_id' </span><span style="color: #007700">=&gt; \</span><span style="color: #0000BB">Drupal</span><span style="color: #007700">::</span><span style="color: #0000BB">currentUser</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">id</span><span style="color: #007700">(),<br>&nbsp;&nbsp;&nbsp; );<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public static function </span><span style="color: #0000BB">baseFieldDefinitions</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityTypeInterface $entity_type</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'fbid'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'integer'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'ID'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'The ID of the FooBar entity.'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setReadOnly</span><span style="color: #007700">(</span><span style="color: #0000BB">TRUE</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'uuid'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'uuid'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'UUID'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'The UUID of the FooBar entity.'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setReadOnly</span><span style="color: #007700">(</span><span style="color: #0000BB">TRUE</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'langcode'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'language'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Language code'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'The language code of theFooBar entity.'</span><span style="color: #007700">));<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'string'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Name'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'The name of the FooBar entity.'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setTranslatable</span><span style="color: #007700">(</span><span style="color: #0000BB">TRUE</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setPropertyConstraints</span><span style="color: #007700">(</span><span style="color: #DD0000">'value'</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'Length' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'max' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">32</span><span style="color: #007700">)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setSettings</span><span style="color: #007700">(array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">''</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'max_length' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">255</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'text_processing' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayOptions</span><span style="color: #007700">(</span><span style="color: #DD0000">'view'</span><span style="color: #007700">, array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'label' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'above'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'string'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'weight' </span><span style="color: #007700">=&gt; -</span><span style="color: #0000BB">6</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayOptions</span><span style="color: #007700">(</span><span style="color: #DD0000">'form'</span><span style="color: #007700">, array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'string'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'weight' </span><span style="color: #007700">=&gt; -</span><span style="color: #0000BB">6</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayConfigurable</span><span style="color: #007700">(</span><span style="color: #DD0000">'form'</span><span style="color: #007700">, </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayConfigurable</span><span style="color: #007700">(</span><span style="color: #DD0000">'view'</span><span style="color: #007700">, </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'type'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'string'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Type'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'The bundle of the FooBar entity.'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setRequired</span><span style="color: #007700">(</span><span style="color: #0000BB">TRUE</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_id'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'entity_reference'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'User ID'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'The ID of the associated user.'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setSettings</span><span style="color: #007700">(array(</span><span style="color: #DD0000">'target_type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'user'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setTranslatable</span><span style="color: #007700">(</span><span style="color: #0000BB">TRUE</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fields</span><span style="color: #007700">[</span><span style="color: #DD0000">'foo_bar_field'</span><span style="color: #007700">] = </span><span style="color: #0000BB">FieldDefinition</span><span style="color: #007700">::</span><span style="color: #0000BB">create</span><span style="color: #007700">(</span><span style="color: #DD0000">'string'</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'First FooBar Field'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDescription</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'One field of the FooBar entity.'</span><span style="color: #007700">))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setTranslatable</span><span style="color: #007700">(</span><span style="color: #0000BB">TRUE</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setPropertyConstraints</span><span style="color: #007700">(</span><span style="color: #DD0000">'value'</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'Length' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'max' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">32</span><span style="color: #007700">)))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setSettings</span><span style="color: #007700">(array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">''</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'max_length' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">255</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'text_processing' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayOptions</span><span style="color: #007700">(</span><span style="color: #DD0000">'view'</span><span style="color: #007700">, array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'label' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'above'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'string'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'weight' </span><span style="color: #007700">=&gt; -</span><span style="color: #0000BB">5</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayOptions</span><span style="color: #007700">(</span><span style="color: #DD0000">'form'</span><span style="color: #007700">, array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'string'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'weight' </span><span style="color: #007700">=&gt; -</span><span style="color: #0000BB">5</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayConfigurable</span><span style="color: #007700">(</span><span style="color: #DD0000">'form'</span><span style="color: #007700">, </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -&gt;</span><span style="color: #0000BB">setDisplayConfigurable</span><span style="color: #007700">(</span><span style="color: #DD0000">'view'</span><span style="color: #007700">, </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$fields</span><span style="color: #007700">;<br>&nbsp; }<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>


Controllers

foo_bar/src/Entity/Form/FooBarFormController.php

Define the form for adding and editing FooBar entity content.
It is called by the '_entity_form' definition in the routing.

Note: You only need to add elements to the $fields[] which are not defined as setRequired(TRUE) and are without setDisplayOptions('form', array()) in your baseFieldDefinitions() .

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Definition of Drupal\foo_bar\Entity\Form\FooBarFormController.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">ContentEntityForm</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Language</span><span style="color: #007700">\</span><span style="color: #0000BB">Language</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">\</span><span style="color: #0000BB">FormStateInterface</span><span style="color: #007700">;<br></span><span style="color: #FF8000">/**<br>&nbsp;* Form controller for the foo_bar entity edit forms.<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">FooBarFormController </span><span style="color: #007700">extends </span><span style="color: #0000BB">ContentEntityForm </span><span style="color: #007700">{<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Overrides Drupal\Core\Entity\EntityFormController::form().<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">form</span><span style="color: #007700">(array </span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">FormStateInterface $form_state</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">/* @var $entity \Drupal\foo_bar\Entity\FooBar */<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form </span><span style="color: #007700">= </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">form</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$entity </span><span style="color: #007700">= </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">entity</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'user_id'</span><span style="color: #007700">] = array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'UID'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">user_id</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">target_id</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#size' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">60</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#maxlength' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">128</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#required' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#weight' </span><span style="color: #007700">=&gt; -</span><span style="color: #0000BB">10</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'langcode'</span><span style="color: #007700">] = array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Language'</span><span style="color: #007700">),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'language_select'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getUntranslated</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">language</span><span style="color: #007700">()-&gt;</span><span style="color: #0000BB">id</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#languages' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">Language</span><span style="color: #007700">::</span><span style="color: #0000BB">STATE_ALL</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'type'</span><span style="color: #007700">] = array(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'hidden'</span><span style="color: #007700">,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getEntityTypeId</span><span style="color: #007700">(),<br>&nbsp;&nbsp;&nbsp; );<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Overrides \Drupal\Core\Entity\EntityFormController::submit().<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">submit</span><span style="color: #007700">(array </span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">FormStateInterface $form_state</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Build the entity object from the submitted values.<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$entity </span><span style="color: #007700">= </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">setRedirect</span><span style="color: #007700">(</span><span style="color: #DD0000">'foo_bar.list'</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$entity</span><span style="color: #007700">;<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Overrides Drupal\Core\Entity\EntityFormController::save().<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">save</span><span style="color: #007700">(array </span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">FormStateInterface $form_state</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$entity </span><span style="color: #007700">= </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">entity</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">save</span><span style="color: #007700">();<br>&nbsp; }<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>


foo_bar/src/Entity/Form/FooBarDeleteForm.php

Confirmation form when deleting foo_bar content

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\foo_bar\Entity\Form\FooBarDeleteForm<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">ContentEntityConfirmFormBase</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Url</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">\</span><span style="color: #0000BB">FormStateInterface</span><span style="color: #007700">;<br></span><span style="color: #FF8000">/**<br>&nbsp;* Provides a form for deleting a foo_bar entity.<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">FooBarDeleteForm </span><span style="color: #007700">extends </span><span style="color: #0000BB">ContentEntityConfirmFormBase </span><span style="color: #007700">{<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">getQuestion</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Are you sure you want to delete entity %name?'</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'%name' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">label</span><span style="color: #007700">()));<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">getCancelUrl</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; return new </span><span style="color: #0000BB">Url</span><span style="color: #007700">(</span><span style="color: #DD0000">'foo_bar.list'</span><span style="color: #007700">);<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">getConfirmText</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Delete'</span><span style="color: #007700">);<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">submit</span><span style="color: #007700">(array </span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">FormStateInterface $form_state</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">delete</span><span style="color: #007700">();<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">watchdog</span><span style="color: #007700">(</span><span style="color: #DD0000">'content'</span><span style="color: #007700">, </span><span style="color: #DD0000">'@type: deleted %title.'</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'@type' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">bundle</span><span style="color: #007700">(), </span><span style="color: #DD0000">'%title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">label</span><span style="color: #007700">()));<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">setRedirect</span><span style="color: #007700">(</span><span style="color: #DD0000">'foo_bar.list'</span><span style="color: #007700">);<br>&nbsp; }<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>


foo_bar/src/Entity/Controller/FooBarListBuilder.php

Define header and row content for the FooBar listing. The 'Operations' links are added automatically from the 'links' definition in the entityType annotation when the parent functions are called.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Contains \Drupal\foo_bar\Entity\Controller\FooBarListBuilder.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">Controller</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">EntityInterface</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">EntityListBuilder</span><span style="color: #007700">;<br></span><span style="color: #FF8000">/**<br>&nbsp;* Provides a list controller for foo_bar entity.<br>&nbsp;*/<br></span><span style="color: #007700">class </span><span style="color: #0000BB">FooBarListBuilder </span><span style="color: #007700">extends </span><span style="color: #0000BB">EntityListBuilder </span><span style="color: #007700">{<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">buildHeader</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$header</span><span style="color: #007700">[</span><span style="color: #DD0000">'id'</span><span style="color: #007700">] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'FooBarID'</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$header</span><span style="color: #007700">[</span><span style="color: #DD0000">'label'</span><span style="color: #007700">] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Label'</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$header</span><span style="color: #007700">[</span><span style="color: #DD0000">'foo-bar_field'</span><span style="color: #007700">] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'FooBarField'</span><span style="color: #007700">);<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$header </span><span style="color: #007700">+ </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">buildHeader</span><span style="color: #007700">();<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * {@inheritdoc}<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">buildRow</span><span style="color: #007700">(</span><span style="color: #0000BB">EntityInterface $entity</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">/* @var $entity \Drupal\foo_bar\Entity\FooBar */<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'id'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">id</span><span style="color: #007700">();<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'label'</span><span style="color: #007700">] = </span><span style="color: #0000BB">l</span><span style="color: #007700">(</span><span style="color: #0000BB">$this</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getLabel</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">), </span><span style="color: #DD0000">'foo-bar/' </span><span style="color: #007700">. </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">id</span><span style="color: #007700">());<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'foo_bar_field'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$entity</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">getFooBarField</span><span style="color: #007700">();<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$row </span><span style="color: #007700">+ </span><span style="color: #0000BB">parent</span><span style="color: #007700">::</span><span style="color: #0000BB">buildRow</span><span style="color: #007700">(</span><span style="color: #0000BB">$entity</span><span style="color: #007700">);<br>&nbsp; }<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>


Field Settings

foo_bar/src/Entity/Form/FooBarSettingsForm.php

Create a settings form for FooBar. Fields can be manage from here.

<span style="color: #000000"><span style="color: #0000BB">&lt;?php<br></span><span style="color: #FF8000">/**<br>&nbsp;* @file<br>&nbsp;* Defines Drupal\foo_bar\Entity\Form\FooBarSettingsForm.<br>&nbsp;*/<br></span><span style="color: #007700">namespace </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">foo_bar</span><span style="color: #007700">\</span><span style="color: #0000BB">Entity</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">\</span><span style="color: #0000BB">FormBase</span><span style="color: #007700">;<br>use </span><span style="color: #0000BB">Drupal</span><span style="color: #007700">\</span><span style="color: #0000BB">Core</span><span style="color: #007700">\</span><span style="color: #0000BB">Form</span><span style="color: #007700">\</span><span style="color: #0000BB">FormStateInterface</span><span style="color: #007700">;<br>class </span><span style="color: #0000BB">FooBarSettingsForm </span><span style="color: #007700">extends </span><span style="color: #0000BB">FormBase </span><span style="color: #007700">{<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Returns a unique string identifying the form.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @return string<br>&nbsp;&nbsp; *&nbsp;&nbsp; The unique string identifying the form.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">getFormId</span><span style="color: #007700">() {<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #DD0000">'foo_bar_settings'</span><span style="color: #007700">;<br>&nbsp; }<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Form submission handler.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @param array $form<br>&nbsp;&nbsp; *&nbsp;&nbsp; An associative array containing the structure of the form.<br>&nbsp;&nbsp; * @param array $form_state<br>&nbsp;&nbsp; *&nbsp;&nbsp; An associative array containing the current state of the form.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">submitForm</span><span style="color: #007700">(array &amp;</span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">FormStateInterface $form_state</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// Empty implementation of the abstract submit class.<br>&nbsp; </span><span style="color: #007700">}<br>&nbsp; </span><span style="color: #FF8000">/**<br>&nbsp;&nbsp; * Define the form used for FooBar settings.<br>&nbsp;&nbsp; * @return array<br>&nbsp;&nbsp; *&nbsp;&nbsp; Form definition array.<br>&nbsp;&nbsp; *<br>&nbsp;&nbsp; * @param array $form<br>&nbsp;&nbsp; *&nbsp;&nbsp; An associative array containing the structure of the form.<br>&nbsp;&nbsp; * @param array $form_state<br>&nbsp;&nbsp; *&nbsp;&nbsp; An associative array containing the current state of the form.<br>&nbsp;&nbsp; */<br>&nbsp; </span><span style="color: #007700">public function </span><span style="color: #0000BB">buildForm</span><span style="color: #007700">(array </span><span style="color: #0000BB">$form</span><span style="color: #007700">, </span><span style="color: #0000BB">FormStateInterface $form_state</span><span style="color: #007700">) {<br>&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'foo_bar_settings'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#markup'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'Settings form for FooBar. Manage field settings here.'</span><span style="color: #007700">;<br>&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br>&nbsp; }<br>}<br></span><span style="color: #0000BB">?&gt;</span></span>

实体类型

原文链接:https://www.drupal.org/docs/8/api/entity-api/entity-types

本文翻译自Last updated on 14 January 2017的版本

  • Drupal 7 - 实体是普通的stdClass类。
  • Drupal 8 - 实体在drupal 8 中是特别类型,每一个entity类型都定义了一个class用于创建指定entity的instance。

Requirements
Entity classes must be placed in the Entity sub-namespace of the module that provides the entity type, e.g. \<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span></span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">[</span>module_name<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">]</span>\<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Entity</span>. This means entity class PHP files may be found in a module's src<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>Entity directory.

The docblock for the class must contain an EntityType annotation that defines the metadata for entities of that type. These include things like the entity type's label, controllers, tables, etc. For a documented list of all the available metadata properties, refer to the \<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Annotation<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityType</span> class.

Naming

Entity type names should be prefixed with the module name if the entity type and module name aren't the same. Prefixing the entity type's class itself is not required since it lives within the namespace of the defining module, provided it is meaningful enough on its own. For example, the entity type for taxonomy terms is named taxonomy_term and the class name is Drupal\<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">taxonomy<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Term</span>.

Interfaces

Drupal 8 recommends you type hint functions and methods with interfaces instead of classes. For example, the generic entity storage hooks type hint with EntityInterface as in <span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">hook_entity_insert</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span>EntityInterface <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$entity</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span> and the node specific storage hooks type hint with NodeInterface as in <span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">hook_node_insert</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span>NodeInterface <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span>.

Entity fields / properties are often very short, storage-centric and not very self-descriptive. Additionally, content entities don't use defined properties for their fields (including base fields like the node title) at all.

Therefore, the recommended approach is to provide an Interface with documented method names. A few rules to follow when doing so:

  • Methods usually have a get/set/is or similar prefix: getSomething(), setSomething($value), isSomething()
  • Only add methods for things that other code is supposed to change. The last changed date of nodes ($node->changed) isn't supposed to be changed, so there is <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">getChangedTime</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span> but no <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">setChangedTime</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span>method.
  • Use self-descriptive method names, for example, the method to access <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token property" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(158, 92, 0);">status</span> is called <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">isPublished</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span>.

Discoverability

To find out which entity types a module provides, refer to the classes in the Entity sub-namespace of that module that have an @EntityType annotation, which also contains the name in the id annotation key.

When trying to find where a given entity type is defined, the first thing to look for is the prefix of the entity type. If a module doesn't follow that naming convention, then it can be found by searching for id <span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">=</span> <span class="token string" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(48, 130, 23);">&quot;$type&quot;</span>. If the class or interface is known instead of the entity type, then the namespace of that indicates where it is coming from.

Example

core<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>modules<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>node<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>src<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>Entity<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>Node<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">.</span>php:

<span class="token keyword keyword-namespace" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">namespace</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>node<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>

<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>ContentEntityBase</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityChangedTrait</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityStorageInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityTypeInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Field<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>BaseFieldDefinition</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Session<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>AccountInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>node<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>NodeInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>user<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>UserInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>

<span class="token comment" spellcheck="true" style="box-sizing: border-box; margin: 0px; padding: 0px; border: none; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(112, 112, 112);">/**
* Defines the node entity class.
*
* @ContentEntityType(
*   id = &quot;node&quot;,
*   label = @Translation(&quot;Content&quot;),
*   bundle_label = @Translation(&quot;Content type&quot;),
*   handlers = {
*     &quot;storage&quot; = &quot;Drupal\node\NodeStorage&quot;,
*     &quot;storage_schema&quot; = &quot;Drupal\node\NodeStorageSchema&quot;,
*     &quot;view_builder&quot; = &quot;Drupal\node\NodeViewBuilder&quot;,
*     &quot;access&quot; = &quot;Drupal\node\NodeAccessControlHandler&quot;,
*     &quot;views_data&quot; = &quot;Drupal\node\NodeViewsData&quot;,
*     &quot;form&quot; = {
*       &quot;default&quot; = &quot;Drupal\node\NodeForm&quot;,
*       &quot;delete&quot; = &quot;Drupal\node\Form\NodeDeleteForm&quot;,
*       &quot;edit&quot; = &quot;Drupal\node\NodeForm&quot;
*     },
*     &quot;route_provider&quot; = {
*       &quot;html&quot; = &quot;Drupal\node\Entity\NodeRouteProvider&quot;,
*     },
*     &quot;list_builder&quot; = &quot;Drupal\node\NodeListBuilder&quot;,
*     &quot;translation&quot; = &quot;Drupal\node\NodeTranslationHandler&quot;
*   },
*   base_table = &quot;node&quot;,
*   data_table = &quot;node_field_data&quot;,
*   revision_table = &quot;node_revision&quot;,
*   revision_data_table = &quot;node_field_revision&quot;,
*   translatable = TRUE,
*   list_cache_contexts = { &quot;user.node_grants:view&quot; },
*   entity_keys = {
*     &quot;id&quot; = &quot;nid&quot;,
*     &quot;revision&quot; = &quot;vid&quot;,
*     &quot;bundle&quot; = &quot;type&quot;,
*     &quot;label&quot; = &quot;title&quot;,
*     &quot;langcode&quot; = &quot;langcode&quot;,
*     &quot;uuid&quot; = &quot;uuid&quot;,
*     &quot;status&quot; = &quot;status&quot;,
*     &quot;uid&quot; = &quot;uid&quot;,
*   },
*   bundle_entity_type = &quot;node_type&quot;,
*   field_ui_base_route = &quot;entity.node_type.edit_form&quot;,
*   common_reference_target = TRUE,
*   permission_granularity = &quot;bundle&quot;,
*   links = {
*     &quot;canonical&quot; = &quot;/node/{node}&quot;,
*     &quot;delete-form&quot; = &quot;/node/{node}/delete&quot;,
*     &quot;edit-form&quot; = &quot;/node/{node}/edit&quot;,
*     &quot;version-history&quot; = &quot;/node/{node}/revisions&quot;,
*     &quot;revision&quot; = &quot;/node/{node}/revisions/{node_revision}/view&quot;,
*   }
* )
*/</span>
<span class="token keyword keyword-class" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">class</span> <span class="token class-name" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Node</span> <span class="token keyword keyword-extends" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">extends</span> <span class="token class-name" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">ContentEntityBase</span> <span class="token keyword keyword-implements" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">implements</span> <span class="token class-name" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">NodeInterface</span> <span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">{</span>
  <span class="token comment" spellcheck="true" style="box-sizing: border-box; margin: 0px; padding: 0px; border: none; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(112, 112, 112);">// ...</span>
<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">}</span>

To get a complete picture of the entities in Drupal 8 we can review the following diagram. This represent the entity classes. To view open in a new tab:

classdrupal_entities.png

实体类型

原文链接:https://www.drupal.org/docs/8/api/entity-api/entity-types

本文翻译自Last updated on 14 January 2017的版本

  • Drupal 7 - 实体是普通的stdClass类。
  • Drupal 8 - 实体在drupal 8 中是特别类型,每一个entity类型都定义了一个class用于创建指定entity的instance。

Requirements
Entity classes must be placed in the Entity sub-namespace of the module that provides the entity type, e.g. \<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span></span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">[</span>module_name<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">]</span>\<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Entity</span>. This means entity class PHP files may be found in a module's src<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>Entity directory.

The docblock for the class must contain an EntityType annotation that defines the metadata for entities of that type. These include things like the entity type's label, controllers, tables, etc. For a documented list of all the available metadata properties, refer to the \<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Annotation<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityType</span> class.

Naming

Entity type names should be prefixed with the module name if the entity type and module name aren't the same. Prefixing the entity type's class itself is not required since it lives within the namespace of the defining module, provided it is meaningful enough on its own. For example, the entity type for taxonomy terms is named taxonomy_term and the class name is Drupal\<span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">taxonomy<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Term</span>.

Interfaces

Drupal 8 recommends you type hint functions and methods with interfaces instead of classes. For example, the generic entity storage hooks type hint with EntityInterface as in <span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">hook_entity_insert</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span>EntityInterface <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$entity</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span> and the node specific storage hooks type hint with NodeInterface as in <span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">hook_node_insert</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span>NodeInterface <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span>.

Entity fields / properties are often very short, storage-centric and not very self-descriptive. Additionally, content entities don't use defined properties for their fields (including base fields like the node title) at all.

Therefore, the recommended approach is to provide an Interface with documented method names. A few rules to follow when doing so:

  • Methods usually have a get/set/is or similar prefix: getSomething(), setSomething($value), isSomething()
  • Only add methods for things that other code is supposed to change. The last changed date of nodes ($node->changed) isn't supposed to be changed, so there is <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">getChangedTime</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span> but no <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">setChangedTime</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span>method.
  • Use self-descriptive method names, for example, the method to access <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token property" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(158, 92, 0);">status</span> is called <span class="token variable" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 120, 190);">$node</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">-</span><span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">&gt;</span><span class="token function" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(6, 71, 113);">isPublished</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">(</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">)</span>.

Discoverability

To find out which entity types a module provides, refer to the classes in the Entity sub-namespace of that module that have an @EntityType annotation, which also contains the name in the id annotation key.

When trying to find where a given entity type is defined, the first thing to look for is the prefix of the entity type. If a module doesn't follow that naming convention, then it can be found by searching for id <span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">=</span> <span class="token string" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(48, 130, 23);">&quot;$type&quot;</span>. If the class or interface is known instead of the entity type, then the namespace of that indicates where it is coming from.

Example

core<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>modules<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>node<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>src<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>Entity<span class="token operator" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">/</span>Node<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">.</span>php:

<span class="token keyword keyword-namespace" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">namespace</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>node<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>

<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>ContentEntityBase</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityChangedTrait</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityStorageInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Entity<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>EntityTypeInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Field<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>BaseFieldDefinition</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Core<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>Session<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>AccountInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>node<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>NodeInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>
<span class="token keyword keyword-use" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">use</span> <span class="token package" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Drupal<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>user<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">\</span>UserInterface</span><span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">;</span>

<span class="token comment" spellcheck="true" style="box-sizing: border-box; margin: 0px; padding: 0px; border: none; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(112, 112, 112);">/**
* Defines the node entity class.
*
* @ContentEntityType(
*   id = &quot;node&quot;,
*   label = @Translation(&quot;Content&quot;),
*   bundle_label = @Translation(&quot;Content type&quot;),
*   handlers = {
*     &quot;storage&quot; = &quot;Drupal\node\NodeStorage&quot;,
*     &quot;storage_schema&quot; = &quot;Drupal\node\NodeStorageSchema&quot;,
*     &quot;view_builder&quot; = &quot;Drupal\node\NodeViewBuilder&quot;,
*     &quot;access&quot; = &quot;Drupal\node\NodeAccessControlHandler&quot;,
*     &quot;views_data&quot; = &quot;Drupal\node\NodeViewsData&quot;,
*     &quot;form&quot; = {
*       &quot;default&quot; = &quot;Drupal\node\NodeForm&quot;,
*       &quot;delete&quot; = &quot;Drupal\node\Form\NodeDeleteForm&quot;,
*       &quot;edit&quot; = &quot;Drupal\node\NodeForm&quot;
*     },
*     &quot;route_provider&quot; = {
*       &quot;html&quot; = &quot;Drupal\node\Entity\NodeRouteProvider&quot;,
*     },
*     &quot;list_builder&quot; = &quot;Drupal\node\NodeListBuilder&quot;,
*     &quot;translation&quot; = &quot;Drupal\node\NodeTranslationHandler&quot;
*   },
*   base_table = &quot;node&quot;,
*   data_table = &quot;node_field_data&quot;,
*   revision_table = &quot;node_revision&quot;,
*   revision_data_table = &quot;node_field_revision&quot;,
*   translatable = TRUE,
*   list_cache_contexts = { &quot;user.node_grants:view&quot; },
*   entity_keys = {
*     &quot;id&quot; = &quot;nid&quot;,
*     &quot;revision&quot; = &quot;vid&quot;,
*     &quot;bundle&quot; = &quot;type&quot;,
*     &quot;label&quot; = &quot;title&quot;,
*     &quot;langcode&quot; = &quot;langcode&quot;,
*     &quot;uuid&quot; = &quot;uuid&quot;,
*     &quot;status&quot; = &quot;status&quot;,
*     &quot;uid&quot; = &quot;uid&quot;,
*   },
*   bundle_entity_type = &quot;node_type&quot;,
*   field_ui_base_route = &quot;entity.node_type.edit_form&quot;,
*   common_reference_target = TRUE,
*   permission_granularity = &quot;bundle&quot;,
*   links = {
*     &quot;canonical&quot; = &quot;/node/{node}&quot;,
*     &quot;delete-form&quot; = &quot;/node/{node}/delete&quot;,
*     &quot;edit-form&quot; = &quot;/node/{node}/edit&quot;,
*     &quot;version-history&quot; = &quot;/node/{node}/revisions&quot;,
*     &quot;revision&quot; = &quot;/node/{node}/revisions/{node_revision}/view&quot;,
*   }
* )
*/</span>
<span class="token keyword keyword-class" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">class</span> <span class="token class-name" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">Node</span> <span class="token keyword keyword-extends" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">extends</span> <span class="token class-name" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">ContentEntityBase</span> <span class="token keyword keyword-implements" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(83, 115, 141);">implements</span> <span class="token class-name" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline;">NodeInterface</span> <span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">{</span>
  <span class="token comment" spellcheck="true" style="box-sizing: border-box; margin: 0px; padding: 0px; border: none; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(112, 112, 112);">// ...</span>
<span class="token punctuation" style="box-sizing: border-box; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 14.7693px; vertical-align: baseline; color: rgb(85, 85, 85);">}</span>

To get a complete picture of the entities in Drupal 8 we can review the following diagram. This represent the entity classes. To view open in a new tab:

classdrupal_entities.png

Drupal 8 的校验API

原文地址:https://www.drupal.org/node/2015613 
 
[术语翻译]
 
API:                        应用程序接口
Validation API:       校验应用程序接口

 

Drupal 8的校验API

最后更新:2014年8月26日, fago创建于2013年6月9日

编辑:jbekker, roderik, fgm, jsruzicka

翻译:半截烟头

 

 

    在Drupal 8, 实体校验已经被移到了一个单独的实体校验API里,并且已从表单校验解耦。将实体校验从表单校验里解耦允许校验实体可以独立于表单提交,比如通过RESTful WEB服务变更。这个新的校验API已经基于Symfony 校验器实现。

 

预览

    校验是由一组约束条件来控制的,比如文本的最大长度或是一组允许值的限制等等。Symfony 有许多有用的约束,Drupal应用更多的Drupal规范扩展了这些约束。Drupal Symfony校验器已经被集成到了类型化数据 API,因此校验约束可以被作为实体字段定义的一部分来应用和定义,并且可以通用地定义为任何类型化数据。

 

使用此API

    校验可以通过在任何类型化数据对象上调用validate()方法来被调用,如下面的例子:

<?php
    $definition = DataDefinition::create('integer')
      ->setConstraint('Range', array('min' => 5));
    $typed_data = \Drupal::typedDataManager()->create($definition, 10);
    $violations = $typed_data->validate();
?>

 

如果传递一个空值校验,将返回一个违规列表。

<?php
  if ($violations->count() > 0) {
    // Validation failed.
  }
?>

违规也是对象,它提供一个翻译过的违规信息给调用者:

<?php
  print $violations[0]->getMessage();
?>

    在这个例子里,一个“范围”约束被指定为数据定义的一部分。然而,基于数据类型,一些额外的约束会默认被这个类自动生成或添加。例如,一个电子邮件类型会添加约束以保证值是一个字符串并且是一个有效的电子邮件地址。所有被应用的约束都可以调用$typed_data->getConstraints()方法来检索。

    在一个类型化数据对象上调用validate()方法是一个获取Symfony校验器和校验数据的快捷方式,而无论这个Symfony校验器对象是否被配置为必须的。$typed_data->validate()等于:

<?php
    return \Drupal::typedDataManager()->getValidator()->validate($typed_data);
?>

校验实体

    实体字段和字段项也都是类型化数据对象,并且能被校验,如下例:

<?php
$violations = $entity->field_text->validate();
?>

这是一个校验一个实体是否为一个整体的例子:

<?php
$violations = $entity->validate();
?>

   违规包含有指向校验失败的属性的属性路径,即相对于此对象,校验是从哪开始的。如:如果示例字段的文本(其位于$field[0]->value)校验失败,在第一个例子里,$violation->getPropertyPath()会返回属性路径如“0.value”,而在第二个例子里,会返回“field_text.0.value”。

 

将约束放到字段项的属性上

    通过setPropertyConstraints()方法,实体字段定义简化了放置约束到独立的字段项属性。在接下来的例子中,字段定义放置了一个最大长度约束到字段项的“值”属性($field[0]->value):

<?php
    $fields['name'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Name'))
      ->setPropertyConstraints('value', array('Length' => array('max' => 32)));
?>

与Symfony 校验器的关系

    Drupal使用了Symfony的约束类和他们的校验逻辑(ConstraintValidator类),但是通过Drupal 8插件系统的来集成他们,因此他们可通过基于注释发现器来发现。这样做的结果是,在类型化数据定义里的约束引用都是插件ID。比如,上例中的“范围”就是指范围约束插件(类)。

    Symfony校验器也被配置使用Drupal翻译类,因此违规信息可以正确的运行t()函数来显示。当Symfony 约束信息使用{{key}}语法来表示信息点位符时,这些信息会被正规的Drupal占位符(表单里的%key)来转换。为保持一致性,更多的Symfony约束暴露于Drupal时,都应该仿效这个方法。

 

文档

更多详情请点此阅读关于Symfony校验器。

 


 

Drupal 8 的类型化数据API

原文地址:https://www.drupal.org/node/1794140

[术语翻译]
API:                        应用程序接口
Typed Data API:    类型化数据应用程序接口
Entity API:              实例化应用程序接口
 

Drupal 8的类型化数据API

2014年2月24日最后更新,Dixon于2012年9月25日创建

Berdir, michaellenahan, chx, alanburke等编辑

 

翻译:半截烟头

 

 

预览

创建“类型化数据应用程序接口”(Typed Data API)是为了给开发者提供一个在不同方式下进行数据交互的一致的方法。此应用程序接口不仅允许你对实际数据(actual data)进行交互,还提供了取得关于实际数据更多信息或元数据的方法。

类型化数据应用程序接口是一个低级别,泛型(generic,通用?)和可重用的面向对象应用程序接口。一个应用程序接口实现它的实体应用程序接口 – Drupal 基本数据模型。

 

为什么我们会需要类型化数据应用程序接口

不象其他许多语言,PHP是一个宽松数据类型语言,它在处理数据时没有明确定义不同的数据类型。因此Drupal也一样。例如:没有一个一致的方法来告诉你一个字段是字符串,整数或时间戳,当然也不能告诉你这些是否可翻译或可访问(有访问许可)。即使你作为一个开发者,恰好知道一个文本字段的值恰好就是一个字符串,也没有一个一致的程序性方法来取得这个信息。这在创建Drupal机器可读的应用程序接口并将数据展示给其他系统的时候,产生了许多问题。

这就需要一个一致性的方法来类型化这个数据,或是描述这个数据。

 

基本的应用程序接口

类型化数据应用程序接口主要提供了三个不同的接口。下面你可以找到这些接口的描述和一些重要的方法(非全部列表)

  • ComplexDataInterface接口

实现这个接口是用于那些由命名属性这样的多数据块组成的数据,这个接口定义了一些基本的方法,包括:

get($property_name)方法

获取属性值。

set($property_name, $value)方法

设置属性值,参数$value必须是这三个接口其中的一个所实例化的实例。

 

  • ListInterface接口

实现这个接口是用于那些由其他顺序化列表所组成的数据,实例化一个其他复杂数据块列表。列表被排序,并且可以包含重复项目,例如:一个多节点引用字段可能包含两次相同的引用。这个接口扩展自ArrayAccess接口。

 

offsetGet($index)方法

此方法继承自ArrayAccess,所以一个单独的项将被从数据组中取出。

$first_item = $items[0];

 

  • TypedDataInterface接口
  • 实现这个接口是用于表示一个单一类型化数据,如字符串,整数等等,这是类型化数据应用程序接口最小的内建块,可以明确地按声明的类型使用。此接口定义了一些基本方法,包括:

 

getValue()方法

取得数据的值。

 

setValue($value)

设置数据的值

 

getDefinition()

 取得关于此数据的信息,如是否为基本类型等等。

 

使用此应用程序接口

[待完成]

一个定义长什么样

[待完成]

 

实体应用程序接口 

Drupal 8 实体API建立于类型化数据API

 

 

实例API如何实现类型化数据API

原文地址:https://www.drupal.org/node/1795854

[术语翻译]
API:                     应用程序接口
Typed Data API: 类型化数据应用程序接口
Entity API:           实例化应用程序接口
 
 

实例API如何实现类型化数据API

最后更新 2014年2月24日,Dixon创建于2012年9月26日

编辑:Berdir, izus, oenie, chx. 登录以编辑此页.

 

翻译:半截烟头

 

 

预览

Drupal 8,实例应用程序接口基于类型化数据应用程序接口。

在此新的实例化应用程序接口中,任何东西都被视为基于此应用程序接口的一个字段,所以这些实例都是可预测的和一致的。

 

理解Drupal的数据模型

首先,在我们一头扎入类型化数据本身之前,我们必须先理解Drupal的数据模型(实例化应用程序接口)是如何被感知的。这很重要,因为这就是类型化数据应用程序接口所来自的地方,并且实例化应用程序接口也已被设计为系统的一个组成部分。

一个实例就是一个复杂的数据块 由其他数据块组成,就象一个由一列项目组成的字段集一样。一个字段项也一样复杂 它也是由更多块数据组成的,如文本值和它的输入格式。然而,到这些东西能够被描述为一些基本数据类型如字符串或整数的时候,这个复杂性也就至此为止。

一个来自Drupal 7的简化例子(这个例子无关语言,因为Drupal 8会用不同的方式来处理):

表一

<?php
//实例很复杂,他们包含其他数据块。
$entity;
//字段不复杂,它们仅包含一个列表项。
$entity->image;
//数据项很复杂,它们包含其他数据块,它们也是可翻译和可访问的(有权限)
$entity->image[0];
//文件编号是一个基本的整数
$entity->image[0][‘fid’];
//备选文本是一个基本的字符串
$entity->image[0][‘alt’];
?>

把它放到一起

下面是一个简化的例子,展示了实例化应用程序接口是如何用类型化数据应用程序接口实现不同的接口的。现实中,实例化应用程序接口扩展了这些接口,增加了一些实例化应用程序接口所需要的方法。仍然地,下面这些语句需要评估是否正确:

表二

 

<?php
// 实例是复杂的
$entity instanceof ComplexDataInterface;
// 属性不复杂,它们只是一个项目列表
$entity->get('image') instanceof ListInterface;
// 项目是复杂的
$entity->get('image')->offsetGet(0) instanceof ComplexDataInterface;
// 这个类型化的数据对象表示备选值
$entity->get('image')->offsetGet(0)->get('alt') instanceof TypedDataInterface;
// 备选值是一个基本的字符串
is_string($entity->get('image')->offsetGet(0)->get('alt')->getValue());
?>

 

 

下面简要概述了实例化应用程序接口如何扩展类型化数据应用程序接口来适应一些附加需求:

表三

<?php
interface EntityInterface extends ComplexDataInterface, TranslatableInterface, AccessibleInterface {
 // ...
}
interface FielditemListInterface extends ListInterface {
 // ...
}
// 注意这扩展了两个接口,解释如下。
interface FieldItemInterface extends ComplexDataInterface, TypedDataInterface {
 // ...
}
// 下面是实际实现方法
// 扩展一个抽象类加入一些常见逻辑
class ImageItem extends FieldItemBase {
 // ...
}
// 扩展一个抽象类加入一些常见逻辑
class String extends TypedData {
 // ...
}
?>

[下面两段需要更多的工作]

 

上面的两个最显着的事情是:

  1. EntityInterface为某些东西扩展一些功能接口如翻译或访问控制能力。这应该是能够很好地自解释的。
  2. FieldItemInterface 扩展ComplexDataInterface和TypedDataInterface两个接口。如早前所解释的,数据项看起来是复杂的,因为它们包含更多的数据块(如文本值和文本项目的格式)。但是同时一个数据项本身也是一个类型化数据块,因此它也有它自己的定义和数据类型。

 

因此,要总结上面这些,包括上面表二,下面这些语句请评估是否正确:

 

表四

<?php
$entity instanceof EntityInterface;
$entity->get('image') instanceof FieldItemListInterface;
$entity->get('image')->offsetGet(0) instanceof FieldItemInterface;
$entity->get('image')->offsetGet(0)->get('alt') instanceof String;
is_string($entity->get('image')->offsetGet(0)->get('alt')->getValue());
?>

 

使用应用程序接口

[本段需要更多例子]

实例化应用程序接口定义了一些魔幻的方法,如__get(),来允许快速简单地访问字段值。因此使用这个应用程序接口非常简单,而且它的语法也提示了许多Drupal 8以前时代的用法。

获取图象的备选文本的实际值将可以按如下方式来做:

表五

<?php
// 最啰嗦的方法
$string = $entity->get('image')->offsetGet(0)->get('alt')->getValue();
// 应用实例化应用程序接口所添加的魔术
$string = $entity->image[0]->alt;
// 应用更多的实例化应用程序接口所添加的魔术, 默认获取列表里的第一个数据项
$string = $entity->image->alt;
?>

上面的例子仅仅为旧的应用程序接口增加了一些漂亮语法。下面的例子示范了这个应用程序接口的真实值来自哪里 检查数据:

表六

<?php
// 根据”命名键”返回一个包含所有字段和它们的定义的数组。例如’image’字段。
$property_definitions = $entity->getFieldDefinitions();
// 根据”命名键”返回一个包含所有属性和它们的定义的数组。例如’file_id’和’alt’属性。
$property_definitions = $entity->image->getFieldDefinition()->getPropertyDefinitions();
// 只返回’alt’属性的定义
$string_definition = $entity->image->getFieldDefinition()->getPropertyDefinition('alt');
?>

基于上面我们所获取的定义,现在我们可以做一些聪明的事情,比如系列化或其他数据处理。我们也可以展现此语义丰富的数据,比如一个JSON-LD终端,然后其他系统就可以理解我们的数据的精要了。

查看https://drupal.org/node/2078241可看到更多关于对实体类型如何定义和使用字段定义。

 

 

 

Drupal 8 菜单API

原文链接:https://www.drupal.org/developing/api/8/menu

 

Drupal8与 Drupal7的菜单系统的对比

Drupal7的菜单系统围绕hook_menu(),它提供了路径和回调函数(控制器)直接的联系,并作为中心枢纽提供不同菜单的菜单项(大多在管理菜单)之间的关联,也为有关页面和上下文链接的不同路径提供选项卡和操作链接。它也做了访问检查,实体荷载等等。对于一个系统来说,这是一个很大的工作量,相对于Drupal8菜单系统。

在Drupal8中,这些功能区域被分成不同的系统。路径与控制器的关联,再加上参数上溯造型(parameter upcasting)和访问检查,现在是在路由系统来处理。该系统可作为Drupal 8网站访问路径的基础。现在Drupal8的菜单系统,是作为不同API菜单项的集合,这些菜单项通过模块以及当地的任务,行动和上下文链接定义。

Drupal 8 表单API

原文链接:https://www.drupal.org/node/2117411

 

Drupal 8 表单API

Druapl 8 表单API大体上和Drupal 7的版本类似, 表现形式仍然是嵌套的数组结构.当然还有分开的表单验证和提交步骤.现在可以添加一些新的(html 5)表单元素了,而如何将这些组件集成到其他Drupal系统中是有一些变化的.

新的(html 5) 表单元素

你可以查看 system_element_info()来知道所有drupal内核支持的表单元素.举一些新的html 5表单元素的例子:'#type' => 'tel', '#type' => 'email', '#type' => 'number', '#type' => 'date', '#type' => 'url', '#type' => 'search', '#type' => 'range'等等.如果你需要的表单元素不仅仅只是能录入任意字符,你应该考虑它们, 因为它们能让设备找到最合适的输入方法.举个例子,你想让用户输入一个电话号码时应该弹出拨号面板. 也有其他表单元素也加入到drupal 8来丰富你的表单.'#type' => 'details' 这是一个带有概括文字的分组元素;'#type' => 'language_select' 这个是一个容易配置的语言选择器;像'#type' => 'dropbutton' 和 '#type' => 'operations'这些下拉链接的控件你可能以及在Views模块里以及见过了,现在它们在drupal 8里面被广泛运用.

概述

表单类实现了\Drupal\Core\Form\FormBuilderInterface接口.一个表单的工作流程是由该接口的buildForm, validateForm和submitForm方法来定义的.当一个表单被请求时,它是被定义承一个可渲染的数组(表单API数组或者简单的说$form数组).$form数组被内部处理转承html代码然后展示给用户.当一个用户提交表单时,请求被发往包含表单的当前地址,Drupal会注意到有POST请求,它先处理表单,接着调用合适的数据验证和提交的代码(没有POST时只是处理表单,展示html).

把表单定义成有结构的数组相比直接写成html代码有很多优势:

  • 所有表单统一的html输出
  • 一个模块定义的表单可以很容易的被另一个模块改变,而不需要做复杂的字符串搜索替换.
  • 可以封装一些复杂包括展示和数据处理的控件,比如文件上传,投票控件等.

定义表单

定义表单必须实现\Drupal\Core\Form\FormBuilderInterface接口.所有表单相关的逻辑如表单的创建,提交,验证都由表单类处理.

有一些不同的预定义父类你可以根据情况选择.绝大多数情况下在创建表单时,你只需要继承下面的类.

  • ConfigFormBase 为创建类似admin/config/system/site-information的系统配置表单.
  • ConfirmFormBase 提供带确认提交功能的表单,比如你要删除一个内容时.
  • FormBase 创建表单最通用的基类.

随便你继承哪个父类,你很可能实现的第一个接口都是

public function getFormId()

这只需要返回一个字符串来作为表单的唯一标识符.最佳的做法是用你的模块名称做其前缀.

举例:

<?php
 
public function getFormId() {
    return
'mymodule_settings';
  }
?>
public function buildForm(array $form, FormStateInterface $form_state) buildForm方法返回一个定义所有表单元素的表单API数组.

举例:

<?php
 
public function buildForm(array $form, FormStateInterface $form_state) {
   
$form['my_text_field'] = array(
     
'#type' => 'textfield',
     
'#title' => 'Example',
    );
.
   
//别忘了调用父类的buildForm方法,这样我们可以利用父类以及实现的功能.
   
return parent::buildForm($form, $form_state);
  }
?>
验证表单

在用户填完表单并提交之后,正常情况是需要验证数据的.用Drupal的表单API来做数据验证是非常简单的,只需要实现\Drupal\Core\Form\FormBuilderInterface接口的validateForm方法.你可以在我们的FormExample类里查看相关例子.

用户提交的数据可以在$form_state['values']里找到.$form_state['values']也是一个数组,它的每一个键值都和你创建表单数组$form时添加的表单元素的名称一一对应(查看FormExample::buildFrom()了解更多). 如果你有一个电话号码的表单项,你可以从$form_state['values']['phone_number']读取到电话号码的值然后用其做自定义的数据验证.

表单验证方法可以用任意合适的PHP代码来判断表单值是否正确,不正确的值话可以报告一个错误事件.下面的例子我们继承了\Drupal\Core\Form\FormBase类,所以我们能用\Drupal\Core\Form\FormBase::setFormError()在特定的表单元素上注册一个错误,并且提供相应的消息来描述该错误.

当表单被提交时,Drupal同时用内部预定义验证方法和我们自定义的验证方法去发现所有的输入错误.然后该表单的html代码被重新生成并带有高亮错误信息.这样用户可以修正错误以再次提交表单.

下面是一个简单的validateForm()方法例子:

<?php
/**
 * {@inheritdoc}
 */
public function validateForm(array &$form, FormStateInterface $form_state) {
  if (
strlen($form_state['values']['phone_number']) < 3) {
   
$this->setFormError('phone_number', $form_state, $this->t('电话号码太短了. 请输入完整的电话号码.'));
  }
}
?>
如果表单验证过程中没有任何错误被注册,Drupal就继续处理表单本身.到这个点的时候Drupal就认为$form_state['values']数组是合法的,然后我们的模块就可以对提交的数据做任何操作了.

提交表单/处理表单数据

最后,我们已经确保可以使用提交的数据了, 我们可以将数据持久化到数据库或者发一封电子邮件,或者其他任何事情. 要这样做我们需要实现FormExample类中\Drupal\Core\Form\FormBuilderInterface的submitForm接口. 像上面的验证方法的用法,用户提交的数据都可以从$form_state['values']数组读取到,并且我们可以认为数据已经验证过,可以直接使用.要获取电话号码的值,只需读取$form_state['values']['phone_numer']就可以了. 下面是一个简单的submitForm方法,它会将我们提交的 'phone_numer'表单项的值用drupal_set_message()显示在页面上.

<?php
/**
 * {@inheritdoc}
 */
public function submitForm(array &$form, FormStateInterface $form_state) {
 
drupal_set_message($this->t('Your phone number is @number', array('@number' => $form_state['values']['phone_number'])));
}
?>
这个处理提交数据的例子有点简单过头.如果你想接触更复杂的例子,请参考drupal内核一些继承FormBase的类. 下面是一个完整的表单类例子: 如果你的模块是/modules/example, 该文件则位于 modules/example/lib/Drupal/example/Form/ExampleForm.php:
<?php
/**
 * @file
 * Contains \Drupal\example\Form\ExampleForm.
 */
namespace Drupal\example\Form;
use
Drupal\Core\Form\FormBase;
use
Drupal\Core\Form\FormStateInterface;
/**
 * Implements an example form.
 */
class ExampleForm extends FormBase {
 
/**
   * {@inheritdoc}.
   */
 
public function getFormId() {
    return
'example_form';
  }
 
/**
   * {@inheritdoc}.
   */
 
public function buildForm(array $form, FormStateInterface $form_state) {
   
$form['phone_number'] = array(
     
'#type' => 'tel',
     
'#title' => $this->t('Your phone number')
    );
   
$form['actions']['#type'] = 'actions';
   
$form['actions']['submit'] = array(
     
'#type' => 'submit',
     
'#value' => $this->t('Save'),
     
'#button_type' => 'primary',
    );
    return
$form;
  }
 
/**
   * {@inheritdoc}
   */
 
public function validateForm(array &$form, FormStateInterface $form_state) {
    if (
strlen($form_state['values']['phone_number']) < 3) {
     
$this->setFormError('phone_number', $form_state, $this->t('The phone number is too short. Please enter a full phone number.'));
    }
  }
 
/**
   * {@inheritdoc}
   */
 
public function submitForm(array &$form, FormStateInterface $form_state) {
   
drupal_set_message($this->t('Your phone number is @number', array('@number' => $form_state['values']['phone_number'])));
  }
}
?>
在Drupal 7里表单生成器的函数名称就是表单的ID, 而在Drupal 8里表单的ID需要由表单类的getFormId方法返回。生成器方法叫做buildForm(), 并且验证和提交都有专门的方法。你在这些方法里写的逻辑,表单数组结构,表单处理过程都和Drupal 7中用法类似。 将表单融入请求 路由系统可以让表单类被提供承路由处理者,也就是说路由系统负责实例化这个类并且调用合适的方法。你得有下面一种路由配置来将该表单融入Drupal网站的URI结构: 如果模块在 /modules/example下, 则文件内容应该在/modules/example/example.routing.yml中: example.form: path: '/example-form' defaults: _title: 'Example form' _form: '\Drupal\example\Form\ExampleForm' requirements: _permission: 'access content' 这个_form的键告诉路由系统,提供的类名称是一个表单类,可以初始化并且处理成一个表单。 注意,要让一个表单工作,写表单类和配置路由是仅需要的两项工作,没任何其他代码要写。 在路由之外取得该表单 虽然drupal 7的drupal_get_form()函数在drupal 8里面以及不见了,但是我们有一个FormBuilder服务,可以用来获取和处理表单。Drupal 8的drupal_get_form()替代品如下:
<?php
$form
= \Drupal::formBuilder()->getForm('Drupal\example\Form\ExampleForm');
?>
方法getForm()接受的参数就是你定义的表单(实现了\Drupal\Core\Form\FormBuilderInterface)的类名.如果你想传入额外的参数给表单,就传在类名称后面。 例子:
<?php
$extra
= '612-123-4567';
$form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);
...
public function
buildForm(array $form, FormStateInterface $form_state, $extra = NULL)
 
$form['phone_number'] = array(
   
'#type' => 'tel',
   
'#title' => $this->t('Your phone number'),
   
'#value' => $extra,
  );
  return
$form;
}
?>
在一些特殊情况下,你可能需要在FormBuilder调用你的类的buildForm()方法前操作表单对象,这种情况下你可以这么做:
<?php
 $form_object
= new \Drupal\mymodule\Form\ExampleForm($something_special); $form_builder->getForm($form_object);
?>
改变表单 在Drupal 8中改变表单和drupal 7中差不多。假设你提供了表单ID,则利用hook_form_alter()和/或hook_form_FORM_ID_alter()来改变表单。
<?php
/**
 * Implements hook_form_FORM_ID_alter().
 */
function example2_form_example_form_alter(&$form, &$form_state) {
 
$form['phone_number']['#description'] = t('Start with + and your country code.');
}
?>
模块名称(example2)加上表单ID(example_form)便可以用来命名hook_form_FORM_ID_alter()钩子。如果你使用过Drupal 7的改变表单相关功能,你会发现在drupal 8中是一样的。

DRUPAL8数据库API - 确认动作的确认窗体库

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/drupal8mo-kuai-kai-fa-drupal8pei-zhi-api-que-ren-dong-zuo-de-que-ren-chuang-ti-ku.htm

原文链接:https://www.drupal.org/docs/8/api/form-api/confirmformbase-to-confirm-an-action

Drupal8开发中,确认表单的构建相当简单,是推荐用户确认动作的推荐方式。与许多形式一样,这一切都是从一条路线开始的。
路由
在一个Drupal8模块的 *.routing.yml文件中,创建一个到表单的路径。在Drupal开发的许多情况下,希望将参数从路径传递到确认表单,就像删除某种内容一样。这可以在下面的例子中看到:
example_module.delete:
path: '/example/{id}/delete'
defaults:
_form: '\Drupal\example_module\Form\ConfirmDeleteForm'
_title: 'Confirm Deletion'
requirements:
_permission: 'administer site configuration'
id: ^\d+$

ID的值通过附加到标准参数列表的参数传递给窗体的buildForm()函数。一个只允许数字ID通过的ReGEX已经被应用在“需求”部分。
注意:路由参数是用户提供的内容,因此不安全。上面的正则表达式保证只传递数字,但是可能需要以某种方式对其他参数进行消毒或验证,以确保不传递恶意内容。

Drupal确认表单
构造了一个扩展确认表单库并实现确认窗体接口的新形式。至少,从确认窗体接口需要实现以下四个功能:
public function submitForm(array &$form, FormStateInterface $form_state);
public function getFormId();
public function getCancelUrl();
public function getQuestion();

Drupal8开发的具体实施过程中,要查看其他什么可以实现,请检查ConfirmFormInterface  API文档。
为了获得要在表单中使用的路由参数,需要在类中创建一个字段来存储它,并且需要用路由参数的附加参数实现buildForm()。

drupal8 确认表单例子
<?php
namespace Drupal\example_module\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Defines a confirmation form to confirm deletion of something by id.
*/
class ConfirmDeleteForm extends ConfirmFormBase {
/**
 * ID of the item to delete.
 *
 * @var int
 */
protected $id;
/**
 * {@inheritdoc}
 */
public function buildForm(array $form, FormStateInterface $form_state, string $id = NULL) {
$this->id = $id;
return parent::buildForm($form, $form_state);
}
/**
 * {@inheritdoc}
 */
public function submitForm(array &$form, FormStateInterface $form_state) {
// @todo: Do the deletion.
}
/**
 * {@inheritdoc}
 */
public function getFormId() : string {
return "confirm_delete_form";
}
/**
 * {@inheritdoc}
 */
public function getCancelUrl() {
return new Url('example_module.another_path');
}
/**
 * {@inheritdoc}
 */
public function getQuestion() {
return t('Do you want to delete %id?', ['%id' => $this->id]);
}
}

这个例子相当简单,有很多方法可以改进。在Drupal8中,使用Drupal的Bartik 主题,结果将是这样的:

 

Drupal 8 路由系统

原文链接:https://www.drupal.org/developing/api/8/routing

Drupal8 的路由系统代替了 Drupal7 的hook_menu.hook_menu将被使用其他子系统替换成创建menu实体,tabs,actions,contextual links

概览:

一个路由的定义是为了使Drupal返回一些内容为目的。例如,默认首页是’/node’的路由。当Drupal收到一个请求时,它会试着在已知的路由里面匹配请求路径。如果路由被找到,那么将会从路由的定义中返回内容。否则,Drupal将会转向404页面。

路由和控制器:

Drupal的路由系统工作在Symfony HTTP 核心基础上。可是,你没有必要为了做一些基础的路由操作而去深刻了解Symfony HTTP核心。下面就是关于路由组件之间的联系图。

路由及控制器处理流程如下:

drupal8routing.png

路由系统负责使用定义在路由中的关系匹配路径的控制器。你可以在路由中传递一个额外的信息到控制器。访问的权限控制也被集成到路由里面了。

Drupal8 的路由和控制器的示例

原文地址:

最后更新于2014年8月17日

你的模块或许只是想在网站指定的路由提供一个功能或仅仅是为了修改或增强已有函数的功能。如果你仅仅是修改或扩展已有功能,你可能没有必要了解路由。可是,如果你想在网站中展示自己的内容或功能,那么路由是模块编写的一个重要环节。下面快速介绍一下路由相关的信息。

基于模块的*.routing.yml文件定义模块的请求行为,其他文件名格式是基于模块名来定义,如example模块的路由文件名为: example.routing.yml.定义了当请求一个路径时Drupal是如何处理的:

example.content:
  path: '/example'
  defaults:
    _content: '\Drupal\example\Controller\ExampleController::content'
    _title: 'Hello World'
  requirements:
    _permission: 'access content'

这个告诉了Drupal8,这个命名为’example.content’(模块名为前缀)的路由已被定义并绑定到了网站的’/example’ 这个URI上。当该路径被访问时,’access content’权限将会启用检查用户的权限,如果有权限,那么ExampleController::content方法将会被调用并展示其内容结果,标题为’Hello’.

如果你使用过Drupal7的菜单构建系统,这里非常相似于Drupal7里面的hook_menu()。(可是,路由系统是不负责管理tabs, actoin links和contextual links)。

ExampleController.php

创建页面的第二部分是输出我们的内容以代替路由中控制器。这些代码可以是PHP4风格函数,但在Drupal8中最佳实践是用控制器类。这个类是严格按照路由文件提供的控制器名来命名类名的,ExampleController.php

Drupal8是使用PSR-0类加载规范来初始化的,然后转向了PSR-4规范。这里可以查看PSR-4相关的版本或修订日志:

PSR-4: Putting it all together
 

PSR-4的细节超出了本章内容范围,但可以查看PSR-4在Drupal8中的命名空间和自动加载方式以了解更多细节。

PSR-4 namespaces and autoloading in Drupal 8
 

上述所说的控制器文件应该被放置到example/src/Controller目录下,并以ExampleController.php文件命名。因此文件的全路径名称看起来应该是这样:

example/src/Controller/ExampleController.php.

内容如下:

<?php
/**
 * @file
 * Contains \Drupal\example\Controller\ExampleController.
 */
namespace Drupal\example\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ExampleController extends ControllerBase {
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('module_handler')
    );
  }
  /**
   * {@inheritdoc}
   */
  public function content() {
    $build = array(
      '#type' => 'markup',
      '#markup' => t('Hello World!'),
    );
    return $build;
  }
}
?>

使用这个路由文件,我们可以在网站上输出一个标题为'Hello World'的可用页面。

路由中使用的不同对象

原文地址:Various objects used in relation with Routing

最后更新于2014年10月14日 15:50:25

  1. 在PHP表单中,一个路由对象仅仅是 yml 路由文件而已。它不包含任何你需要的方法,仅仅是 getters ,被叫作是值对象(类似JSON)。这不是一个需要你去获取或者花大部分时间与之打交道的对象,大多数情况下方法都用路由名称或参数来替代。
  2. 一个 RouteMatch 对象包含了路由的名称(正如之前所说,这个对象比路由本身更加重要)、路由对象、参数和与匹配相关的未处理参数。例如如果匹配是针对 node/123 的,那么路由会自动包含 /node/{node} 的路由对象,未处理参数是 [1 => 123],但参数实际上会变成 [1=> Node::load(123)]。除了getters, RouteMatch 不包含任何可用方法。
  3. CurrentRouteMatch 对象也是一个 RouteMatch对象,但是它包含了当前正在使用的路由对象,它被当成服务来使用并同时被传递给每一个 hook_help方法。再则,你仅可以在该对象上使用 getters 。
  4. 当前的 Url 对象包含了产生 URL 的路由名称、参数和 options ,如 'absolute' 或 'query' (不像 RouteMatch 那样已经转换了参数并且去除了 options)。当获取系统路径亦或是获取可被添加到 #type href 渲染键的渲染数组时,Url 对象显得尤为有用。

词汇表:

  • RouteMatch: \Drupal\Core\Routing\RouteMatchInterface
  • CurrentRouteMatch: \Drupal\Core\Routing\CurrentRouteMatch

路由中的访问检查

原文链接:https://www.drupal.org/node/2122195

最后更新于2014年6月12日。

使用permissions/roles简单检查

路由的数据结构允许你对访问的路由使用and/or关联角色来限定角色的访问权限。你可以结合这些权限检查方法使用access_mode选项来选择全部/部分权限。可以在路由结构中的requirements和options节查看。(The route data structure allows you to specify permissions and/or roles that are required to access the route. You can combine these access check methods with an any or all relation using the access_mode option. See the requirements and options section in the route structure.)

路由中自定义权限检查(Custom access checking on routes)

路由中的自定义访问检查 有时候,仅仅依靠权限add/or role是不够的,你需要做的自定义访问检查路由。为了实现这一目标,实现一个类accesscheckinterface用以检查访问。如果你的模块为Example,这是一个简单的实现例子。文件放置在

lib/Drupal/example/Access/CustomAccessCheck.php:
<?php
/**
 * @file
 * Contains \Drupal\example\Access\CustomAccessCheck.
 */
namespace Drupal\example\Access;
use Drupal\Core\Access\AccessCheckInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\HttpFoundation\Request;
/**
 * Checks access for displaying configuration translation page.
 */
class CustomAccessCheck implements AccessCheckInterface {
  /**
   * A user account to check access for.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $account;
  /**
   * Constructs a CustomAccessCheck object.
   *
   * @param \Drupal\Core\Session\AccountInterface
   *   The user account to check access for.
   */
  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }
  /**
   * {@inheritdoc}
   */
  public function access(Route $route, Request $request) {
    // Check permissions and combine that with any custom access checking needed. Pass forward
    // parameters from the route and/or request as needed.
    return $this->account->hasPermission('do example things') && $this->someOtherCustomCondition();
  }
}
?>

额外的,如果需要添加一个访问类作为服务,那么需要在example.services.yml中添加一个入口(Additionally, you have to add the access class as a service by adding an entry in example.services.yml)

services:
  example.access_checker:
    class: Drupal\example\Access\CustomAccessCheck
    arguments: ['@current_user']
    tags:
      - { name: access_check, applies_to: _example_access_check }

通过添加’_example_access_check’标志到路由的requirements节里面,这个路由将会调用这权限检查(通过applies()方法里面匹配实现). 然后access()方法将会执行自定义的访问检查。也可以从第三方服务里面使用数据,按需要在任何时间完成需要的自定义的数据检查。(By adding the '_example_access_check' flag to the requirements section of a route, that route will have this access checker invoked (through the matching implemented in the applies() method). Then the access() method performs the custom access check. You can use data from third party services, custom data checking or even the time of day, as needed.)

There is of course no need to define a custom requirements flag for your access checker, if you can identify the right routes to work with without doing so.

简单的自定义权限检查器(Simplified custom access checker)

在一些情况下,如在单个路由入口中你需要添加一个访问权限检查器。

在这些情况下,你可以极大简化过程。

在这些情况你可以直接在路由定义中指定控制器,如下:

example:
  path: '/example'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _custom_access:  '\Drupal\example\Controller\ExampleController::access'
<?php
/**
 * @file
 * Contains \Drupal\example\Controller\ExamleController.
 */
namespace Drupal\example\Controller;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpFoundation\Request;
/**
 * Builds an example page.
 */
class ExampleController {
  /**
   * A user account to check access for.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $account;
  /**
   * Constructs a CustomAccessCheck object.
   *
   * @param \Drupal\Core\Session\AccountInterface
   *   The user account to check access for.
   */
  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }
  /**
   * Checks access for a specific request.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request to check access for.
   */
  public function access(Request $request) {
    // Check permissions and combine that with any custom access checking needed. Pass forward
    // parameters from the route and/or request as needed.
    return $this->account->hasPermission('do example things') && $this->someOtherCustomCondition();
  }
}
?>

在这种情况下,access方法和类中的控制器相同,因此就没有必要增加一个独立的服务入口。(In this case, the access method is on the same class as the controller itself so there is no need for a separate service entry.)

在控制器内容方法里面的权限检查(Access checking in the controller content method)

因为一些原因如果你想控制器的内容方法中添加一些访问检查,你可以在路由定义中设置_access: true并且在控制器的内容方法作访问检查动作内容。如果你要使用这个方法,确保页面不正确或访问拒绝的时能返回合适的Symfony异常。这个获取内容由于上面的方法的解耦肯定优于混合页面逻辑访问。(Finally, if you need or want to do access checking in the controller content method for some reason, you can set _access: truein the route definition and do the access checking in the controller content method. If you take this approach, make sure to return the proper Symfony route exceptions if the page is not found or access should be denied. The above methods where access is decoupled from content are definitely preferred over mixing page logic with access.)

CSRF访问检查(CSRF access checking)

CSRF保护已经被集成到路由访问系统里面,并且被用作任何URLs执行动作或操作并且不会使用表单调用。在之前的Drupal版本中,在查询参数里面有必要添加一个密钥到URL中,并在callback或access中检查。现在你可以简单的在路由定义中添加一个’_csrf_token’到requirements节里面。按照如此做,系统将会自动添加一个token到查询字符串,并使用这个token为你做访问检查。(CSRF protection is now integrated into the routing access system, and should be used for any URLs that perform actions or operations that do not use a form callback. In previous versions of Drupal, it was necessary to add a generated token as a query parameter to a URL and check this token manually in either the callback or the access callback. Now you can simply use the '_csrf_token' requirement on a route definition. Doing so will automatically add a token to the query string, and this token will be checked for you.)

example:
  path: '/example'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _csrf_token: 'TRUE'

提示:为了让token被添加,链接必须通过路由名(‘example’)使用url_generator服务产生而不是人工构造路径。(Note that, in order for the token to be added, the link must be generated using the url_generator service via route name ("example" here) rather than as a manually constructed path.)

路由结构

定义路由的最简单的方法是创建一个module.routing.yml文件。每个路径被定义为module_name.route_name形式的机器名称(book.render)具有以下属性:routes的结构:

  • path (required): 你可以通过在花括号引用他们,使用的动态特性。(例如,路径: ‘/node/{node}/outline’) 。(路由的URL是以一个反斜杠开头(例,path:’/book’).你可以在大括号里面使用动态属性来引用它们。(例,path: ‘/node/{node}/outline’).这将会通过参数转换传递到控制器/表单里面.(如下).如果你想在路由里面有一个可选的参数可以参看 这里
  • defaults (required): 定义路由的默认属性。提供一个以下指定如何生成输出:
    • _content: 典型的如(classname::method),以返回一个可渲染的数组。这会被渲染进HTML中并作为Drupal主题的主要内容。 Drupal8路由和控制器的示例介绍
    • _controller: 仅返回一个对象,不会被主题化。如Symfony\Component\HttpFoundation\Response object(Another callable returning a Symfony\Component\HttpFoundation\Responseobject. )因为没有HTML(如,XML种子)或仅有一些HTML片断。这会被直接发送,没有主题或区块会被添加。
    • _form: 这一个期望实现Drupal的formInterface。 可以查看Drupal8表单的示例。 
    • _entity_view: 这个值是entity_type.view_mode.在给定的视图模式下,通过路径和渲染模式下查找一个实体。如: _entity_view:node.teaser。 在视图模式下将返回一个{node}的渲染数组。查看实体视图控制器提供的实体视图页面
    • _entity_list: 这个值是entity_type。使用期望实体的EntityListController提供一个实体列表。例:_entity_list: view_mode返回一个视图模式的渲染数组。
    • _entity_form: 和_form相似,但它提供的是一个可编辑的实体表单。实体表单的处理被定义在实体的元数据(注释)中。例如,_entity_form: node.default将会显示节点表单。在node.default中node引用了实体的ID,’default’引用了表单的控制器键。

    一些可选的选项参数:

    • _title (optional): (可选)该路由的页面标题。可以不同于菜单标题。(The page title for the route. May differ from the menu link title.)
    • _title_context (optional): :(可选)用于传递给t()标题文本附加上下文信息。(Additional context information for the title text passed along to t().)
    • _title_callback (optional): (可选)一个PHP调用(通常是classname::method)返回的网页标题的路线。(A PHP callable (typically classname::method) returning the page title for the route.)

    最终,如果任何静态参数被传入控制器中,在默认的数组中提供它。命名其他名字和控制器中方法中的一致。如下。

  • 如果你最后可能添加的可选的参数,因此foo/bar/{baz}也应该让foo/bar的baz值和你在默认数组中设置的值一致。

  • requirements (required): 确定必须满足什么条件才能授予访问路径。是以下一个或多个数组:(Determines what conditions must be met in order to grant access to the route. Is an array of one or more of the following:)
    • _permission: 一个权限字符串(例如,_permission: 'access content’)。A permission string (e.g., _permission: 'access content').
    • _role: 一个特定的Drupal的角色,比如“管理员”。您可以通过指定多个,”或“+”和逻辑(但是使用基于权限的访问限制在可能的时候)。(A specific Drupal role, eg. 'administrator'. You can specify multiple ones via "," for OR and "+" for AND logic. Note that, since the roles available may differ between sites, it's recommended to use permission based access restriction when possible.)
    • _access: TODO.
    • _entity_access: 在一般的情况下,实体是路由的一部分,检查一定的访问级别可以在批准访问(例如,_entity_access:”node.view”)。你也可以指定实体如何应验证(例如,node:\d+)。这是有益的如果你有一个路由/foo/ {node}和/foo/bar的一个节点是节点ID。/ foo/bar不匹配/ foo / {node}因为bar不是数字(见例book.routing.yml)。(In the case where an entity is part of a route, can check a certain access level before granting access (e.g., _entity_access: 'node.view'). You can also specify how the entity should be validated (e.g., node: \d+). This is useful if you have a route /foo/{node} and /foo/bar where {node} is a node id. /foo/bar won't match /foo/{node} because bar isn't numeric (see book.routing.yml for an example).)
    • _custom_access: TODO. See Access checking on routes for details.
    • _format: 用这个来检查请求类型。例如,你可以_format:JSON只匹配请求的接受报头为JSON的请求。(Use this to check the type of the request. For example, you can have _format: jsonand only match requests where the Accept header is json.)
    • _module_dependencies: 可以使用这个键来指定一个或多个模块所需要的服务路径。你可以将模块名称+(加号)的关系,(逗号)的关系。例如,_module_dependencies:’node+search’意味着需要两个模块,如果_module_dependencies:’node,search’需要其中一个就可以。如在info.yml中已经依赖于其他模块,在这里就没有必要再写。然而,可选的依赖,路由是只有那些可选的依赖的模块也使提供的,这是一个有用的选项。(Optionally use this key to specify one or more modules that are required to serve this path. You can combine module names with a + (plus) for an AND relationship or , (comma) for an OR relationship. For example, _module_dependencies: 'node + search'means both node and search are needed, _module_dependencies: 'node, search'means either node or search are needed. If your module already depends on other modules for its behaviour (via info.yml dependencies), there is of course no need for specifying the dependency here as well; however for optional dependencies, where routes are provided only if those optional dependent modules are also enabled, this is a useful option.)
    • _csrf_token: TODO. See Access checking on routes for details.
  • options (optional): 附加选项如何路由应相互作用。常见的选项:(Additional options on how the route should interact. Common options are:)
    • _access_mode: (ANY / ALL (default) 如果给出了多个需求(requirements),指定他们应该如何联合。(If multiple requirements are given, specifies how they should be combined.)
      If you choose ALL, all requirements access checkers have to ALLOW access, otherwise on ANY just one has to ALLOW. If any of the access checkers return KILL, access will be denied immediately.
    • _theme: TODO
    • parameters: 如果请求中参数不匹配任何实体名,你可以翻译(转换)这个参数到一个实体。并且你不能使用两个相同的参数。对于没有匹配的实体,你可以使用{user}。( If your parameter name from the path doesn't match any entity name you can translate the parameter to an entity here. And, as you can't have two parameters with the same name, let's say {user}, you can map the unmatched entity here.)
      • second_user:  路径的参数名称,在这种情况下它的第二个参数。(The parameter name from the path. In this case it's {second_user}.)
        • type: 这是我们告诉什么实体使用。在这个例子中,我们就可以写的entity:user,给用户实体匹配参数。 (This is where we tell what entity to use. In this example we can just write entity:user, to map the parameter to the user entity.)

这是一个更复杂的注释的例子:

book.routing.yml

# Each route is defined by a machine name, in the form of module_name.route_name.
#
book.render:
  # The path always starts with a leading forward-slash.
  path: '/book'
  # Defines the default properties of a route.
  defaults:
    # For page callbacks that return a fully-themed HTML page use _content.
    _content: '\Drupal\book\Controller\BookController::bookRender'
  # Require a permission to access this route.
  requirements:
    _permission: 'access content'
book.export:
  # This path takes dynamic arguments, which are enclosed in { }.
  path: '/book/export/{type}/{node}'
  defaults:
    # Because this route does not return HTML, use _controller.
    _controller: '\Drupal\book\Controller\BookController::bookExport'
  options:
    # Ensure *both* requirements below are checked to grant access to the route.
    _access_mode: 'ALL'
  requirements:
    _permission: 'access printer-friendly version'
    # Ensure user has access to view the node passed in.
    _entity_access: 'node.view'

将参数传递给控制器(Passing arguments to controllers)

参数传递给控制器 在default节中,不一定所有传递给控制器的参数都会使用_下划线开始命名。恰当的命名你的控制器参数如下所示:(All keys under the defaultsection which do not start with an underscore will be passed in as arguments to the controller. Name your arguments appropriately for the arguments of the controller. For example a routing.yml file with the following:)

example.content:
  path: '/example'
  defaults:
    _content: '\Drupal\example\Controller\ExampleController::content'
    custom_arg: 12
  requirements:
    _permission: 'access content'

将传递参数$custom_arg给控制器,所以你的内容的方法可以使用参数$custom_arg:

<?php
  // ...
  public function content(Request $request, $custom_arg) {
    // Now can use $custom_arg (which will get 12 here) and $request.
  }
?>

动态替换现有路由或添加新路由

原文链接:Altering existing routes and adding new routes based on dynamic ones

一条路由 ―― 不管是静态地定义在 YAML 文件(参考示例)中,还是动态地创建在使用动态路由中,都可以被替换。你可以使用一个触发了 RoutingEvents::ALTER事件的 EventSubscriber 对象来修改一个 RouteCollection。

替换现有路由

创建路由之后(例如一个模块被启用或缓存被清空的时候),RoutingEvent::ALTER事件将触发路由替换过程,\Drupal\Core\Routing\RouteSubscriberBase基类包含了对该事件的监听器。你可以实现 替换路由(RouteCollection $collection) 中的方法以实现对现有路由的覆盖。 下面这个例子替换了用户模块中的两条路由,在你的模块中使用 src/Routing/RouteSubscriber.php这个文件。

<?php

/**
 * @file
 * Contains \Drupal\example\Routing\RouteSubscriber.
 */

namespace Drupal\example\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  public function alterRoutes(RouteCollection $collection) {
      // Change path '/user/login' to '/login'.
      if ($route = $collection->get('user.login')) {
        $route->setPath('/login');
      }
      // Always deny access to '/user/logout'.
      // Note that the second parameter of setRequirement() is a string.
      if ($route = $collection->get('user.logout')) {
        $route->setRequirement('_access', 'FALSE');
      }
    }
  }

}
?>
\Drupal\example\Routing\RouteSubscriber::alterRoutes

方法是一个订阅者对象,因为它继承自 RouteSubscriberBase。因此,该类的对象必须注册事件发布服务。 在你的模块中使用 example.services.yml文件(example的名字由你决定)。

services:
  example.route_subscriber:
    class: Drupal\example\Routing\RouteSubscriber
    tags:
      - { name: event_subscriber }

基于现有动态路由添加新路由

你可以使用 alterRoutes()方法添加动态路由。如果添加的路由比较独立,那么推荐的方法是使用简洁的路由回调方案而不是实现一个订阅者类。但是,如果添加的路由需要依赖于别的动态路由,那么你就得实现一个继承自 RouteSubscriberBase的类了。确保在实现 getSubscribedEvents()方法的时候权衡好分量。the configuration translation RouteSubscriber 有个很好的例子。

路由参数

Drupal8 的路由支持占位符,该占位符将被 URL 中的动态值替代。这个占位符可以在控制器的方法中直接当作一个同名变量使用。比如 example.routing.yml的这个例子:

example.name:
  path: 'example/{name}'
  defaults:
    _content: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _permission: 'access content'

这个 {name}占位符是 URL 里的一个别名,在控制器方法中可以被当作 $name变量使用,如下:

<?php
class ExampleController {  
  // ...
  public function content($name) {
    // name 是一个字符串值
    // 处理 $name 包含的内容
  }
}
?>

通常在 PHP 代码中变量取什么名字无关紧要,但在这儿情况就不一样了:方法的参数表中的变量名必须和占位符保持一致。如果参数名和占位符别名一样,那么值就会被传递进来,参数的顺序可以随意。

参数自动转换

当在 URL 中使用节点 ID 时,ID 会被“参数转换系统”自动转换为节点对象。详情请参看 Parameter upcasting in routes

路由参数向上转型

原文链接:Parameter upcasting in routes

Drupal8 的路由中的占位符会被 URL 的动态值替代。命名这些占位符之后,系统会转换(上转型)这些值为实际的对象实例。例如如果一个节点的路径是 '/node/{node}',这里 {node}是一个占位符。“参数转换系统(ParamConverter system)”自动处理该参数并将之转换为节点对象的实例。

Drupal 8中JavaScript的辅助工具

原文链接:https://www.drupal.org/node/1973218

JavaScript的辅助工具

这些工具在Drupal 8里正积极发展着,本文档内容可能过时。

听觉上更新发布页面

许多页面的更新通过颜色变化和动画等视觉方式来体现。为了使页面的更新在非可视化方式上变得明显,Drupal 提供了一种Drupal.announce 的Javascript方法。这种方法在页面上创建了一种名为aria-live的元素。屏幕阅读用户代理会读取附加到该节点后的文本内容。

Drupal接收由音频UA读取的字符串,你还可以设置第二个参数:优先级。下面是几个例子:

Drupal.announce('Entering edit mode');
Drupal.announce('Please fill in your user name', 'assertive');
Drupal.announce('You look beautiful today.');

两个公认的优先级值是’polite’和’assertive’,’assertive’是默认值。

‘assertive’状态会中断当前的任何话语,而’polite’状态不会中断用户代理。当页面上的许多内容立刻改变的时候,它们可以不被读取。该公布功能会自行连接多个调用并一次性读取多个字符串,以确保多个同步的更新版本可以面向终端用户。

约束Tab键

对于一些交互功能,你可能想要向不可见用户指引页面上的凸显元素。例如,当启用全局编辑模块时,语境模块会限制连接上下文的Tab键。

这些模块通过Drupal.TabbingManager的Javascript功能来实现Tab键的约束。要想在页面上约束Tab键,应调用Tab键的管理功能,调用方法如下所示:

var tabbingContext = Drupal.TabbingManager.constrain($('.contextual-toolbar-tab, .contextual'));

有一组元素被传递给constrain方法,按下Tab键,光标只能在这组元素中有tab功能的元素中移动。

要想删除tab键约束,tabbingContext对象必须调用若、release方法。如下所示:

tabbingContext.release();

Overlay模块使用两个function函数来实现Tab键的初始化和释放,如下所示:

/**
* 使overlay之外的元素不可通过tab键到达
*/
Drupal.overlay.constrainTabbing = function ($tabbables) {
  // If a tabset is already active, return without creating a new one.
  if (this.tabset && !this.tabset.isReleased()) {
    return;
  }
  // 保留仅在overlay和工具栏之间的链接
  this.tabset = Drupal.TabbingManager.constrain($tabbables);
  var self = this;
  $(document).on('drupalOverlayClose.tabbing', function () {
    self.tabset.release();
    $(document).off('drupalOverlayClose.tabbing');
  });
};
/**
*
*/
Drupal.overlay.releaseTabbing = function () {
  if (this.tabset) {
    this.tabset.release();
    delete this.tabset;
  }
};

一次只能激活一个Tab键约束,当一个Tab键约束正在运行却调用另一个Tab键约束,此时之前运行的Tab键约束会被禁用继而运行新的Tab键约束。你只需关注你的模块所控制的Tab键约束即可。如果另一个模块覆盖了你的Tab键约束然后释放了它,你原来模块的Tab键约束会被重新运用。如果你的模块释放正在被禁用的Tab键约束(这意味着其他模块拥有活跃的Tab键约束),那么结果你可以想象,它将无法重新运行。

Devel的可访问性

Devel的访问(devel_a11y)模块的创建,是为了帮助访问之外的实施、理解和测试功能。

它为浏览器提供了公告的控制台日志记录和Tab键约束的可视化。

例如,下图就展示了如何帮助可视化Tab键约束:

tabbingmanager.visualize.png

JAVASCRIPT API 总览

翻译者:长风Drupal开发

翻译地址:http://www.5188jxt.com/technology/javascript-api-zong-lan.htm

原文链接:https://www.drupal.org/docs/8/api/javascript-api/javascript-api-overview

 

可用的JavaScript库在Drupal 8中发生了很大变化。JavaScript文件,在Drupal 7中的.info文件中引用,现在被引用到.YML文件中。
在Drupal 8中,的样式表(CSS)8,stylesheets (CSS) and JavaScript (JS) 是通过系统加载的Drupal模块(代码)和Drupal8主题:asset libraries。
Drupal8使用高级原则:Drupal8只加载你告诉它应该加载的assets (CSS和JS)。Drupal8不是在载所有页面上的assets (CSS/JS) ,因为这对前端性能很不利。
可配置的JavaScript与drupalSettings(与Drupal7中的Drupal.settings类似)的继承是可用的。但是,为了使drupalSettings 可以用于JavaScript文件:我们必须声明对它的依赖。
Drupal.behaviors
当使用jQuery时,对于大多数情况来说,将$(document).ready()函数中的几乎所有代码包装起来是标准的,如下所示:
$(document).ready(function () {
// Do some fancy stuff.
});

这确保了我们的代码只在DOM加载后以及所有元素都可用的情况下才运行。然而,Drupal8有另一种更好的方法:使用Drupal.behaviors and once()的功能。如果使用得当,这将确保您的代码运行在正常页面负载和数据加载时,由Ajax(或BigPi管!)-但不是应该避免的像load()这样的jQuery方法,因为Drupal.behaviors对将无法加载除ajax()之外的函数。Drupal.behaviors对象本身是Drupal8对象的属性,当我们希望我们的模块/主题添加新的jQuery行为时,最好的方法是简单地扩展这个对象。
一个真实的基本的例子
Drupal.behaviors.myBehavior = {
attach: function (context, settings) {
// Using once() to apply the myCustomBehaviour effect when you want to run just one function.
$('input.myCustomBehavior', context).once('myCustomBehavior').addClass('processed');
// Using once() with more complexity.
$('input.myCustom', context).once('mySecondBehavior').each(function () {
if ($(this).visible()) {
$(this).css('background', 'green');
}
else {
$(this).css('background', 'yellow').show();
}
});
}
};

定义为Drupal.actions属性的任何对象都将在DOM最初加载时以及在任何AJAX调用之后调用.()方法。drupal.js有一个$(document).ready() 函数,调用Drupal.attachBehaviors() 函数,该函数依次循环调用Drupal.behaviors对象的每个属性,所有这些都是由各种模块如上声明的函数,并将文档作为上下文传入。在Ajax加载中,除了上下文仅是加载Ajax调用的新内容之外,发生了同样的事情。(而且由于BigPipe在内部构建在AJAX系统之上,所以任何与AJAX系统一起工作的东西都会与BigPipe一起工作。)

每当调用附加行为时,就会触发Drupal行为。传入的上下文变量通常可以让您更好地了解正在处理什么DOM元素,但是它不是确定是否再次处理某个元素的方法。使用带有“context”的jQuery选择器是一个很好的实践,因为只有给定的上下文被搜索,而不是整个文档。当在Ajax请求之后附加行为时,这一点变得更为重要。

注意:因为Drupal8使用jQuery.noConflict()并在需要时只加载JavaScript文件,所以要使用jQuery和jQuery的$符号,必须在MODULE.libraries.yml中的库定义中包括jQuery和Drupal作为依赖项,并在函数.libraries.yml周围添加包装器。所以整个JavaScript文件看起来都是这样的:

(function ($, Drupal) {
Drupal.behaviors.myModuleBehavior = {
attach: function (context, settings) {
$('input.myCustomBehavior', context).once('myCustomBehavior').each(function () {
// Apply the myCustomBehaviour effect to the elements only once.
});
}
};})(jQuery, Drupal);

要将jQuery作为自定义库的显式依赖项添加一次,请添加core/jquery.once,如下面的MODULE.libraries.yml或THEME.libraries.yml所示:
foobar:
js:
js/foobar.js: {}
dependencies:
- core/jquery
- core/jquery.once

JAVASCRIPT API - Drupal8 引用JavaScript

翻译者:长风Drupal开发

翻译地址:长风http://www.5188jxt.com/technology/drupal8-include-javascript.html

原文链接:https://www.drupal.org/docs/8/api/javascript-api/add-javascript-to-your-theme-or-module

一、Drupal8引用JavaScript的前提

有一个在Drupal8中可以正常运行的模块或者主题。

二、Drupal8引用JavaScript基本步骤

这里有两个基本步骤向你的drupal8主题或者drupal8模块来添加Javascript 
1、定义一个library
2、添加这个library到一个实体

 

1、定义一个library
在Drupal8主题或者Drupal8模块的根目录创建一个*.libraries.yml的文件,例如example.libraries.yml,内容如下:
example:
version: 1.x
js:
js/example.js: {}

备注:
1、根元素exmple通常是模块或者主题的名字,也可以是你想用的其他任何名字,
2、定义library的版本
3、使用js元素 引用JavaScript
4、在JavaScript后面的{}中,你可以定义一些属性,例如
external:bool(告诉Drupal8这个js是不是外部的资源)
header:bool(告诉Drupal8是否在header中加载脚本,而不是footer);
minified:bool(告诉Drupal8是否需要压缩脚本);
preprocess:bool(告诉Drupal8是否需要预处理);

 

三、引用js文件


根据你在Drupal8开发中的实际需求,可以有不同的方式引用library。library总是通过Drupla8模块或者Drupal8主题进行注册的,
module: hook_element_info_alter()
module: hook_page_attachments()
theme: template_preprocess_HOOK()
twig: {{ attach_library(‘example/example’) }}
hook_page_attachments() 和template_preprocess_HOOK()都是相似的,如下:

 

function example_preprocess_node__page(array &$variables) : void {
// Theme name: 'example', library name: 'example'. 
$variables['#attached']['library'][] = 'example/example';
}

hook_element_info_alter()的例子如下,如果你希望在所有radio按钮的地方加载libarary,
function d8_theme_element_info_alter(array &$info) : void {
if (!isset($info['radio'])) {
$info['radio']['#attached']['library'][] = 'example/example';
}
}

四、引用依赖

如果你的Drupal8 JavaScript依赖于其他的资源,比如jQuery或者Drupal settings,你可以使用依赖属性 

 

example:
version: 1.x
js:
js/example.js: {}
dependencies:
- core/jquery
- core/drupalSettings

状态(State)API

原文:https://www.drupal.org/developing/api/8/state

最后更新时间2014年8月19日。原文由heyrocker于20129月17日创建。
参与原文编辑的有 targoo, benjy, mpp, webchick

状态API为开发者提供了一个用来保存系统状态信息的地方。状态信息在如下几个方面区别于配置(configuration):

  • 它是每个环境特有的。
  • 你不会想要把状态信息部署到不同的环境中。
  • 你可以重启系统,这样会导致丢失所有的状态信息,但是环境配置信息还在。

一个典型的关于状态的例子是上一次cron任务执行的时间。这个时间状态是某个环境特有的,你不会把它用于系统部署中去。状态API是一个简单的用来存储此类信息的机制,这些信息在以前的版本中基本上都存储在变量(variables)系统当中。

典型用法:

  • Get a value(获取一个值):
    $val = \Drupal::state()->get('key');
  • Get multiple key/value pairs(获取多个键值对):
    $pairs = \Drupal::state()->getMultiple($keys);
  • Set a value(设置一个值):
    \Drupal::state()->set('key','value');
  • Set multiple values(设置多个值):
    \Drupal::state()->setMultiple($keyvalues);
  • Delete a value(删除一个值):
    \Drupal::state()->delete('key');

对于需要人工进行编辑或者需要在不同环境中进行共享的数据,应使用\Drupal::config()来实现。

 

如何把变量(variables)升级到状态系统

原文:https://www.drupal.org/node/1787318

最后更新时间2013年6月13日。原文由heyrocker于2012年9月17日创建。
参与原文编辑的有twistor, Sylvain Lecoy, LinL, miro_dietiker

原有的变量必须转换为使用新的配置系统API/存储机制来实现。

指导原则

  1. 在进行转换工作的过程中,每次只转换一个变量。
    • 确定要转换的变量的名称。
    • 在整个Drupal项目源码中搜索该变量名称以确定哪些地方需要修改。
  2. 使用简短准确的状态名称。这些名称总体上可保持原样,除非原本的名称不准确或者某种意义上会引起误解。
  3. 状态系统通过调用Drupal::state()函数进行初始化。使用get(),set() 和 delete()方法来与状态系统进行交互。Drupal::state()函数返回一个状态对象,所以你可以这样使用
    <?php
     $data
    = Drupal::state()->get('my_state_data');
    ?>
  4. 获取数据时,状态系统并没有像变量系统一样可以指定默认值。但是你可以在函数返回FALSE的时候指定一个默认值,用来标记数据不存在。具体用法如下
    <?php
     $state
    = Drupal::state()->get('my_state') ?: 'Nothing there';
    ?>

    注意:如果布尔值FALSE或者整数0是状态变量的合法值,那么这种情况需要特殊处理。

  5. 下面的例子展示了如何把变量转换成状态:

    Drupal 7

    <?php
      variable_set
    ('my_data', 'foo');
     
    $data = variable_get('my_data', 'bar');
     
    variable_del('my_data');
    ?>

    Drupal 8
    <?php
      Drupal
    ::state()->set('my_data', 'foo');
     
    $data = Drupal::state()->get('my_data') ?: 'bar';
     
    Drupal::state()->delete('my_data');
    ?>
  6. 这里变量名称做了改动以显示是哪个模块创建了该变量。键值必须与配置系统使用相同的名字空间策略。如下所示:
    • cron_last应改为 system.cron_last
    • node_cron_last应改为 node.cron_last
    • menu_masks应改为 menu.masks
  7. 升级的方式应由变量本身的特性决定。如果变量值在从Drupal 7到8的升级过程中保持一致,那么该变量必须在升级时迁移到Drupal 8。示例如下:
    <?php
    /**
     * 把install_task及install_time变量迁移至状态API。
     *
     * @ingroup state_upgrade
     */
    function system_update_8022() {
     
    update_variables_to_state(array(
       
    'install_task' => 'system.install_task',
       
    'install_time' => 'system.install_time',
      ));
    }
    ?>

    然而如果变量值会在缓存清理过程中被重建或自然地在升级过程中被重建,那么这些Drupal 7的变量应被删除。示例如下:

    <?php
    /**
     * 删除drupal_js_cache_files变量。
     *
     * @ingroup state_upgrade
     */
    function system_update_8023() {
     
    update_variable_del('drupal_js_cache_files');
    }
    ?>
  8. 卸载时删除状态:
    /core/modules/comment/comment.install
    <?php
    function comment_uninstall() {
    ...
     
    // 删除状态。
     
    Drupal::state()->delete('comment.node_comment_statistics_scale');
     }
    ?>
  9. 为升级测试添加测试覆盖:
    为升级测试预先填充测试数据
    /core/modules/system/tests/upgrade/drupal-7.state.system.database.php
    <?php
    db_merge
    ('variable')
      ->
    key(array('name' => 'node_cron_comments_scale'))
      ->
    fields(array('value' => serialize(1.0 / 1000)))
      ->
    execute();
    ?>

    检查新的值是否已生效
    /core/modules/system/lib/Drupal/system/Tests/Upgrade/StateSystemUpgradePathTest.php
    <?php
        $expected_state
    ['comment.count_scale'] = array(
         
    'value' => 1.0 / 1000,
         
    'variable_name' => 'node_cron_comments_scale',
        );
    ?>

迁移的源

迁移配置实体[entity]总是包含一个源的属性值。 该源的属性值是一个关联数组,其中包含插件的名称和配置。 只有源插件的属性值是必须存在的,其他一些插件可能没有任何配置。

source: plugin: d6_node

迁移过程

原文链接:Migrate process >
迁移过程
迁移的属性值配置描述了目的地是如何将属性值从源数据一个一个(property-by-property)构造来的。 过程值是一个关联数组,是每个目的地的属性值。 每个属性值相关联的值描述了目的地属性值是如何创建的。 核心支持最常见情况下的速记。 不太常见的情况下,一个更详细的语法或者不能通过以这种方式表达的,需要编写一个自定义插件。
速记
简单复制
获取插件(get plugin) 用于从一个源属性值复制值。 与所有其他进程插件,可以不用显式命名即可使用它。 例如,复制源属性的值主题(subject)到目的地标题(title)。
process:
  title: subject

创建一个插件
目的地可能是由一个插件(除了隐式获得(get))创建的。 在这种情况下,属性值与目的地相关联的属性值包含一个关联数组插件,使用属性值来识别插件,对于的特殊的的插件,可能需要其他的属性值。 在这个示例中,我们使用 迁移插件 ( 将属性值传递到迁移插件之前,源数据作者(sourse:author)首先使用获取(get)插件获得访问源的路径
process:
  uid:
    plugin: migration
    id: users
    source: author

完整过程
有时,源值必须通过传递多个插件的属性值来得到目的地的属性值和结构。 在这种情况下,属性值和目的地相关的是一个关联数组列表,每个都包含至少一个 插件的属性值及其配置,该配置和上述的单个插件的类似。 传入的源值传递给第一个插件,输出的属性值再传递到第二个插件的输出属性值,以此类推(不应该理解为等等)。 例如,考我们虑如何传递Drupal 6文本格式为可见用户(visible-user)的名字到一个单独的Drupal 8机器名: 过滤器格式化机器的名字首先是应用创建的标签 ,第一个machine_name插件创建的机器名称,然后再使用重复数据删除插件。
process:
    format:
        -
            plugin: machine_name
            source: name
        -
            plugin: dedupe_entity
            entity_type: filter_format
            field: format
这也就是说源属性值命名为名称(name)是传递到 机器(machine_name)插件 ,将原始字符串转换成小写字母数字(加下划线)的名称。 因为这可能会导致相同的机器名称传入不同的的字符串,我们需要唯一的机器名过滤格式(filter_format)实体,接下来我们调用一个 dedupe_entity插件 。 dedupe_entity插件没有指定源;机器名(machine_name)插件的结果隐式传递到dedupe_entity插件,这也包含了 entity_type和 filed配置的属性值, dedupe_entity插件的结果,作为最后一个过程,被分配到目的地属性值格式中。
嵌套的值
如果你想设置 $destination['display_settings']['label']['format'] 或者从源中读取, $source['display_settings']['label']['format'] 你需要使用 display_settings:label:format. 比如:
process:
  source: 'display_settings:label:format'
不要忘单引号。
处理多个属性值 许多插件是一个属性值值作为输入。 迁移系统自动识别该属性值是通过一个列表,而不是通过插件重复性的调用每一个属性值。
无源(属性值)
如果目的地没有属性值,建议通过如下方式增加迁移:
process:
  foo: { }

首先来表示一个空数组( 也可以使用[])。 系统识别和处理空数组比较特殊,该过程也没有设置foo的属性值。 然而,这可以使得一个分析工具或UI识别迁移,意识到这是一个空属性值,不希望它是集。没有这些,可能发出的一个警告:目的地属性值会不确定。
参考常量(const value)相关设置为空,而不是设置一个值。

过程插件get

原文链接Process plugin: get
过程插件get
通常, 获得get插件是每个流程的开始,通常是通过速记添加到另一个插件的源属性值。
get插件有一个配置的属性值source。该值通常是一个源属性值,当运行这个插件后,过程值将被设置为该属性的值。 即: br>

process:
    bar:
        plugin: get
        source: foo
<code>

<br>
只会复制源foo的属性值到目标foo的属性,由于geet是默认的过程插件,因此他可以速记为这样的:

<code>
process:
    bar: foo

get插件还支持源属性值的列表。 如果该列表包含一个空元素,那么将使用当前的属性值。这使得当一个源属性值为一个空字符串作为它的名称时而获得该源是不可能的。
get也支持复制目的地的属性值。 这些都是@符号来表示:
process:
    foo:
        plugin: machine_name
        source: baz
    bar:
        plugin: get
        source: @foo
复制目的地foo的属性值到目标属性变得简单起来。 foo的配置也包括出于示范说明的目的。
正因为如此,如果你的源或目标属性实际上始于一个@符号,你需要两个该(@)开始符号。 这意味着,如果一个目的地属性值开始于@符号,你想引用它,你就需要三个@符号,一个显示目的地,两个是为了避免实际的@符号。
process:
    @foo:
        plugin: machine_name
        source: baz
    bar:
        plugin: get
        source: @@@foo
这中情况应该极少发生。

常量

原文链接Constant values

常量


有时你需要一个常量。 可能会用来也许是连接插件或者一些只是国定的值。 在这种情况下,首先需要在源中定义一个常量,然后过程中的的任何地方使用它。下面的示例设置id的属性值为常量node_search:

source:
  constants:
    id: node_search
process:
  id: constants/id
注意, 常量属性值没有什么特别的。如果包含一个constants的属性值,如果用另一个名字,例如defaults。换句话说,下面的代码示例和上面的作用相同:
source:
  defaults:
    id: node_search
process:
  id: defaults/id
因为源中这样的定义提供的默认值是从源中每一行检索(没有深度合并)。

Drupal 8 插件 API

原文链接:Plugin API in Drupal 8

插件是小型的可插拔功能模块。 拥有类似功能的插件属于同一种插件类型

Drupal包含很多插件和插件类型。例如,'Field widget' (字段小工具)就是一种插件类型,而具体的字段类型就是插件。管理员用户可以从字段类型插件列表中选择并设置字段所使用的类型。

D8的插件系统提供了一套指导原则和可重用的代码组件,使开发者能够公开他们的可插拔组件,如需要,还能够通过用户界面管理这些组件。

插件是由模块定义的:一个模块可以提供不同类型的插件,不同的模块可以提供各自特定类型的插件。

概述

插件系统由三种基本元素构成:

  1. 插件类型

    插件类型是一个控制中心类,它决定了插件如何被系统发现和实例化。这个类型会描述其下所属插件的核心目标,例如后端缓存,图片操作,区块等等。

  2. 插件发现模式

    插件发现是指在可用代码库中查找具有特定类型的、合适的插件实例的过程。(Plugin Discovery is the process of finding plugins within the available code base that qualify for use within this particular plugin type's use case.)

  3. 插件工厂

    插件工厂负责使用给定的用例来实例化具体插件。

此外,插件系统还包含一些特定场景下比较有用的组件:

  • 插件衍生

    插件衍生允许使用一个通用插件代替多个插件。这在用户输入的数据可能影响可用插件的情况下非常有用。例如,假设菜单是用一个插件来展示的,当管理员创建了新的菜单,那么新菜单也能够被该插件展示,而不需要去创建一个额外的插件来展示它。考虑到帮助文档,尤其是用例的帮助文档,既能被输出,也能被应用到别的地方(原文:allowing for help text specific to the use case to be rendered and utilized.),因此插件衍生也支持在用户界面中用多个插件来代替一个插件。插件衍生工具的主要目的是将部分已配置的插件作为优先级插件使用,以免与其他用户界面的插件产生混淆,从而减少管理者使用这些插件的负担。

  • 修饰器发现模式

    修饰器发现是另外一种发现模式,为了包装已存在的发现方法。核心目前提供cacheDecorator来缓存被选择了插件类型的(插件的)发现过程。如果需要的话,这种模式也可以扩展到其他情况下。(原文:A discovery decorator is another available discovery method meant to wrap an existing discovery method. Core currently supplies the cacheDecorator which will cache the discovery process that is chosen for a plugin type. This pattern could be expanded to other use cases if necessary.)
  • 插件映射

    插件映射允许映射一些东西(通常是一个字符串)到一个特定的插件实例。插件类型能够使用这种方法返回一个基于自定义名称的完全配置和初始化的插件,而不需要手动使用API去初始化和配置插件的实例。

本文将就这些概念给出深入的讨论,最佳实践和代码示例。

为何使用插件

原文链接: Why Plugins?

插件有点像PHP原生接口外加一点扩展:插件系统能够(通过神奇的命名空间)发现每一个接口的实现类,(默认情况下使用注解来)处理元数据并为那些插件类提供工厂。

插件实现相同的接口,却提供截然不同的行为——就像裁剪效果无法代替缩放效果(至少对于最终用户来说——插件系统使用相同的方式处理这两种扩展,这是非常必要的)。另外,如果你的接口期待实现类的行为一致而内部结构不同(就像database cache和memcache之于缩放和裁剪,不具有类似的不同之处),只需要在service.yml文件中定义,而不是使用插件系统。

基于注解的插件

原文链接:Annotations-based plugins

Drupal 8 中大部分插件使用注解来注册和描述元数据。也有一些插件类型由核心提供:

  • Entities (未来可能不作为插件)
  • Blocks (在 */lib/Drupal/*/Plugin/Block/* 查看更多示例)
  • 字段格式器,字段小工具>(在 */lib/Drupal/*/Plugin/field/* 查看更多示例)
  • 所有Views插件(在 */lib/Drupal/*/Plugin/views/* 查看更多示例)

你应该在除此之外的文档中去查看一些实际案例。

psr-4

插件使用的注解被注册在PHP文件中使用psr-4标准,Drupal核心也是使用这个标准。

要注册插件,基于你的Drupal module root放置文件:src/Plugin/$plugin_type/Example.php

例如:core/modules/ckeditor/src/Plugin/CKEditorPlugin/Internal.php

为了告诉系统这是一个插件,你需要为你的class添加如下注释:

<?php
/**
 * @Plugin(
 *
 * )
 */
?>

使用这种annotations class的一个具体的插件是UserNameUnique,在 core/modules/user/src/Plugin/Validation/Constraint/UserNameUnique.php文件中

这个注解仅包含id和label。

<?php
/**
 * Checks if a user name is unique on the site.
 *
 * @Plugin(
 *   id = "UserNameUnique",
 *   label = @Translation("User name unique", context = "Validation")
 * )
 */
class UserNameUnique extends Constraint {
...
}
?>

为何使用注解?

相对于其他的发现机制,注解的元数据存在于同一个文件,并作为该插件的实现类的一个组成部分。这使得插件更容易被找到,也更容易被自定义,仅需简单地复制一个现有的插件。

注解允许复杂的结构化数据,你也可以指明某些字符串是被翻译的。很多情况下插件的相关联的自定义注解类既能作为文档,也能够为元数据设置默认值。

另外,注解能额外提升Drupal的性能,它使Drupal在发现插件时使用很少的内存。原来的实例中每个类都有一个getinfo()方法,类似Drupal 7 的test classes。这意味着每一个类都必须被加载到内存来获取其信息,并且内存不会被释放除非请求结束,这会大大增加PHP对Peek Memory的需求。相反,Drupal解析注解的实现只是简单的对文件的文本进行分词,而不是作为PHP文件加载,所以使内存的使用率降到最低。

注解语法

简而言之,注解是一种天然的key-value数据结构,包括嵌套的。

注解的语法来自于Doctrine项目(具体参考http://docs.doctrine-project.org/en/2.0.x/reference/annotations-referenc...),尽管Drupal的语法稍有不同,比如每个值后面都换行,但这任然有用。

  • 必须以插件id开始,相当于Drupal 7 的机器代码,作为插件的唯一键。
  • 顶级的KEYS可以使用双引号
  • 下级的KEYS必须使用双引号
  • 只能使用双引号,不能使用单引号,否则抛出异常
  • 值可用的数据类型
    • String:必须使用引号(例如:"foo"
    • Numbers:不能使用引号(例如:21)——使用了引号会被转化成字符串
    • Boolean:不能使用引号(例如:TRUE 或 FALSE)——使用了引号会被转化成字符串
    • Lists:使用大括号,末尾不要用逗号
      base = {
        "node",
        "foo"
      }
    • Maps:使用大括号,用等号分隔键和值,末尾不要用逗号
      edit = {
        "editor" = "direct"
      }
  • 可以使用常量

自定义注解类

基础的插件注解能被用于发现(插件),但是如果你想在插件定义中添加默认值和文档,你可以使用自定义注解类。

我们来看一个实际案例,plaintext格式器,位于text/src/Plugin/field/formatter/TextPlainFormatter.php

它使用自己的注解类,FieldFormatter,继承自\Drupal\Component\Annotation\Plugin

<?php
/**
 * Plugin implementation of the 'text_plain' formatter.
 *
 * @FieldFormatter(
 *   id = "text_plain",
 *   label = @Translation("Plain text"),
 *   field_types = {
 *     "text",
 *     "text_long",
 *     "text_with_summary"
 *   },
 *   edit = {
 *     "editor" = "direct"
 *   }
 * )
 */
class TextPlainFormatter {
?>

在你自己的插件类型中使用注解

如果你自己写了一个插件类型,并且想要使用注解,只需要在你的plugin manager里面引用AnnotatedClassDiscovery——见下面代码。

AnnotatedClassDiscovery构造函数的第一个参数是这个插件类型被存放的子目录/子命名空间。所以下例中,插件会被在$module/src/Plugin/field/formatter文件夹中搜索到。

<?php
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
class
FormatterPluginManager extends PluginManagerBase {
 
/**
   * Constructs a FormatterPluginManager object.
   *
   * @param array $namespaces
   *   An array of paths keyed by their corresponding namespaces.
   */
 
public function __construct(array $namespaces) {
   
// This is the essential line you have to use in your manager.
   
$this->discovery = new AnnotatedClassDiscovery('Plugin/field/formatter', $namespaces);
   
// Every other line is a good practice.
   
$this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
   
$this->discovery = new AlterDecorator($this->discovery, 'field_formatter_info');
   
$this->discovery = new CacheDecorator($this->discovery, 'field_formatter_types', 'field');
  }
}
?>

注入的命名空间来自于依赖注入容器。例如FieldBundle:

<?php
/**
 * @file
 * Contains Drupal\field\FieldBundle.
 */
namespace Drupal\field;
use
Symfony\Component\DependencyInjection\ContainerBuilder;
use
Symfony\Component\HttpKernel\Bundle\Bundle;
/**
 * Field dependency injection container.
 */
class FieldBundle extends Bundle {
 
/**
   * Overrides Symfony\Component\HttpKernel\Bundle\Bundle::build().
   */
 
public function build(ContainerBuilder $container) {
   
// Register the plugin managers for our plugin types with the dependency injection container.
   
$container->register('plugin.manager.field.widget', 'Drupal\field\Plugin\Type\Widget\WidgetPluginManager')
      ->
addArgument('%container.namespaces%');
   
$container->register('plugin.manager.field.formatter', 'Drupal\field\Plugin\Type\Formatter\FormatterPluginManager')
      ->
addArgument('%container.namespaces%');
  }
}
?>

调用你自定义的插件管理器,你需要在调用构造函数的时候注入命名空间。

<?php
 $type
= new CustomPluginManager(\Drupal::getContainer()->getParameter('container.namespaces'));
?>

D8 插件发现模式

原文链接:D8 Plugin Discovery

插件发现是Drupal根据给定类型查找插件的过程。每种插件类型都必须设置一个发现方法(说明在plugin manager 文档中)。

插件的发现组件实现DiscoveryInterface接口,定义了每个发现类必须有的方法。

<?php
/**
 * @file
 * Contains \Drupal\Component\Plugin\Discovery\DiscoveryInterface.
 */
namespace Drupal\Component\Plugin\Discovery;
/**
 * Defines the plugin discovery interface.
 */
interface DiscoveryInterface {
 
/**
   * Gets a specific plugin definition.
   *
   * @param string $plugin_id
   *   A plugin id.
   *
   * @return array
   *   A plugin definition.
   */
 
public function getPluginDefinition($plugin_id);
 
/**
   * Gets the definition of all plugins for this type.
   *
   * @return array
   *   An array of configuration definitions.
   */
 
public function getPluginDefinitions();
}
?>

有四种不同的核心发现类型。

  1. StaticDiscovery
    StaticDiscovery允许发现类自身直接注册插件。一个protected变量$definitions保存所有的通过public方法setDefinition()注册的插件定义。所有通过这种方式定义的插件都能够在plugin manager documentation中被列出。
  2. HookDiscovery
    HookDiscovery类允许插件发现使用Drupal的hook_component_info()/hook_component_info_alert()模式。有了这个发现器,插件管理器可以调用信息钩子来获取可用插件列表。
  3. AnnotatedClassDiscovery
    AnnotatedClassDiscovery类使用包含插件定义的注解名称,比如,@Plugin@EntityType,在插件的文档部分发现插件,最小化插件发现阶段的内存使用率。AnnotatedClassDiscovery类的构造函数带有参数,$subdir,指明这个插件类型的sub-directory/sub-namespace。AnnotatedClassDiscovery类会扫描插件目录里面这些sub-directory目录里面的符合PSR-0规范的类来查找插件。
  4. YamlDiscovery
    YamlDiscovery允许插件在YAML文件中被定义。 Drupal核心使用该文件进行本地任务和本地操作。

发现装饰器(原文:Discovery Decorators)

发现装饰器是一个为了提供额外功能而封装了另一个发现机制的类(WiKi:Decorate pattern)。发现装饰器遵循与常规发现器类一样的接口,但是它的目的是与另一个发现器串联。发现装饰器的__construct方法需要一个DiscoveryInterface类型的参数和一些其他的必要参数。核心包含两个发现装饰器,我们先来看看CacheDecorater。

Drupal\Core\Plugin\Discovery\CacheDecorator

我们接下来讨论下这里存在的各种方法和它们的功能。

public function __construct(DiscoveryInterface $decorated,$cache_key = NULL);

如上所述,这个方法拥有一个兼容DiscoveryInterface接口的变量$decorated。这可以是任何一种前面讨论过的发现器类。除此之外还有一个变量$cache_key,当调用cache()->get()时会用到。

public function __getPluginDefinition($plugin_id);

如果你有看过任何其他发现器类的代码,应该会熟知这个方法。在CacheDecorator的情况下,我们会检查看看是否有这个已加载的缓存版本,如果我们不这样做,那么它将手动

Drupal 8 区块 API

原文链接: Block API in Drupal 8

概述

Drupal 8 的区块实际上是由两个独立的API结构组成的,延续了Drupal一直以来的用户体验。这两个API结构分别是:Block Plugin API,一个独立的可复用的API;区块实体API,用于区块控制位置和可见性。

通过Block Plugin API创建区块

在你自定义的模块代码中创建区块需要了解Plugin API,更具体的说是基于注解的插件探测(方式/模式)(原文: Annotations based plugin discovery)。这是Drupal 8用来查找你自定义的区块的机制。

在模块中创建自定义区块包含以下步骤:

让区块对Drupal和用户可见

假设你的模块叫 fax,依照PSR-4标准,你自定义的区块的代码应该放到 fax/src/Plugin/Block/之中,并基于它包含的类来命名。比如类名为 FaxBlock,那么文件层次应该像 fax/src/Plugin/Block/FaxBlock.php这样。

FaxBlock.php中定义一个名为 FaxBlock的类,如下例:

<?php
namespace Drupal\fax\Plugin\Block;
use
Drupal\block\BlockBase;
/**
 * Provides a 'Fax' block.
 *
 * @Block(
 *   id = "fax_block",
 *   admin_label = @Translation("Fax block"),
 * )
 */
class ChadBlock extends BlockBase {
   
// Override BlockPluginInterface methods here.
}
?>

注解中的'id'属性是你的区块的机读名称,唯一,能被其他代码引用。注解'admin_label'定义了显示名称。

两个最常重写的方法:

BlockPluginInterface::build()- 该方法返回一个渲染后的数组,包含你想要在区块中显示的内容。

BlockPluginInterface::access()- 该方法根据自定逻辑控制区块的可见性。

添加自定义配置选项

你可以通过重写 BlockPluginInterface::blockForm()BlockPluginInterface::blockSubmit()方法,然后使用BlockBase::setConfigurationValue()BlockBase::getConfiguration()方法来添加自定义配置选项到区块当中。

下例,在blockForm()中添加了一个textfield,然后在blockSubmit()方法中保存了用户填入的值。在blockForm()方法中,你可以看到数据是如何被你的代码获取和使用的。

<?php
/**
 * Provides a 'Fax' block.
 *
 * @Block(
 *   id = "fax_block",
 *   admin_label = @Translation("Fax block"),
 * )
 */
class ChadBlock extends BlockBase {
   
// Access and build methods here ...
  /**
   * {@inheritdoc}
   */
 
public function blockForm($form, &$form_state) {
   
$form = parent::blockForm($form, $form_state);
   
//Retrieve existing configuration for this block.
   
$config = $this->getConfiguration();
   
// Add a form field to the existing block configuration form.
   
$form['fax_block_settings'] = array(
     
'#type' => 'textfield',
     
'#title' => t('Fax number'),
     
'#default_value' => isset($config['fax_block_settings']) ? $config['fax_block_settings'] : '',
    );
    return
$form;
  }
 
/**
   * {@inheritdoc}
   */
 
public function blockSubmit($form, &$form_state) {
     
// Save our custom settings when the form is submitted.
   
$this->setConfigurationValue('fax_block_settings', $form_state['values']['fax_block_settings']);
  }
}
?>

同样,你可以再build()方法中调用BlockBase::getConfiguration()方法来接收配置参数并显示给用户。也可以在access()方法中执行更复杂的逻辑来决定是否显示这些配置参数。

Drupal 8服务与依赖注入

原文地址:Services and dependency injection in Drupal 8

Drupal 8 引入了服务的概念,用来解耦可复用的功能,并且可以通过在服务容器中注册这些服务,让它们可插拔与可替换。身为开发者,最佳的方式就是通过服务容器来访问Drupal提供的所有服务,这样可以保证遵循系统的解耦特性。在Symfony 2的文档中对服务有非常好的介绍。

对开发者而言,服务用来执行类似访问数据库,发送邮件等操作。我们不使用PHP原生的MySQL函数,而是通过服务容器来使用Drupal提供的核心服务来执行这些操作,这样我们的代码可以很简单的访问数据库,而不需要考虑数据库是MySQL还是SQLite,同样,发邮件时也不需要考虑是通过SMTP还是其他方式。

核心服务

核心服务在CoreServiceProvider.php 与 core.services.yml 中定义。如:

...
language_manager:
    class: Drupal\Core\Language\LanguageManager
    arguments: ['@language.default']
  ...
  path.alias_manager:
    class: Drupal\Core\Path\AliasManager
    arguments: ['@path.crud', '@path.alias_whitelist', '@language_manager']
  ...
  string_translation:
    class: Drupal\Core\StringTranslation\TranslationManager
  ...
  breadcrumb:
    class: Drupal\Core\Breadcrumb\BreadcrumbManager
    arguments: ['@module_handler']
  ...

某个服务可以依赖于其他服务。譬如在上面的例子中,path.alias_manager在arguments参数中指定依赖服务 path.crud, path.alias_whitelist 与 language_manager。可以通过@服务名的方式来定义一个服务的依赖,如@language_manager。当Drupal中的某代码请求path.alias_manager服务时,服务容器会保证path.crud, path.alias_whitelist 与 language_manager 服务可以传递给path.alias_manager的构造函数,因此它会先请求上面的每一个服务。依次地,language_manager依赖于language.default,等等等。

Drupal包含大量的服务,获取该列表的最好方法是查看CoreServiceProvider.php 与 core.services.yml  文件。

服务容器(或者叫依赖注入容器)是一个管理服务实例的PHP对象。Drupal的服务容器是在Symfony服务容器的基础上建立的,关于文件结构、特殊字符、可选依赖等相关内容文档可以查看Symfony 2服务容器文档

通过依赖注入来访问服务

在Drupal 8中,依赖注入是访问服务的首选方式,并且应该尽可能的使用。服务通过构造函数参数或者setter方法的方式进行传递,而不是直接调用全局的服务容器。很多由核心模块提供的控制器以及插件类都是使用此方式,并且在运行中被视作是优质资源。

Drupal全局类用于全局函数中。然而,Drupal 8的控制器、插件等是基于类的。其首先方式是将依赖的服务以构造函数参数的方式进行传递,或者以服务setter方法来注入,而不是调用全局的服务容器。

将某个对象依赖的服务显式传递,称为依赖注入。在很多情况下,依赖都是通过构造函数进行显式传递。如:路由访问检测在服务创建时插入当前用户,而当前请求对象则在访问检测时传递。你也可以通过setter方法来设置依赖。

在全局函数中访问服务

Drupal全局类提供静态方法来访问一些最常用的服务,如:Drupal::moduleHandler()返回模块处理程序服务,Drupal::translation()返回字符串翻译服务。如果你调用的服务没有专用方法,你也可以使用Drupal::service()来获取任何已定义的服务。

例子:使用专用方法\Drupal::database()来访问数据库服务

<?php
// Returns a Drupal\Core\Database\Connection object.
$connection = \Drupal::database();
$result = $connection->select('node', 'n')
  ->fields('n', array('nid'))
  ->execute();
?>

 

例子:使用通用的\Drupal::service()方法来访问date服务

<?php
// Returns a Drupal\Core\Datetime\Date object.
$date = \Drupal::service('date');
?>

 

理想情况下,你应该尽量少的使用全局函数,并且重构控制器、事件监听、插件等。适当的方式应该是使用依赖注入;查看下面的文档。

Symfony 2的文档中均有代码范例。

定义你的服务

你可以使用example.services.yml文件来定义自己的服务,其中example就是你定义服务所在的模块名称。文件的格式与core.services.yml相同。

有多个子系统需要你定义服务,如:自定义路由访问检测类自定义参数转换,或者定义一个插件管理器,这都需要将你的类注册为服务。

也可以使用 $GLOBALS['conf']['container_yamls']来增加用于定义服务的YAML文件,只是这样做十分少见。

Drupal 7全局函数与Drupal 8服务对比

我们用运行一个模块钩子所需要的代码来对比一下Drupal 7与8的区别。在Drupal中,你会使用module_invoke_all('help')来运行所有hook_help钩子,因为我们是在代码中直接调用module_invoke_all()函数,那么其他人就很难在不改变Drupal核心函数的情况下修改Drupal运行钩子的方式。

在Drupal 8中,用ModuleHandler代替了module_*函数。因此在Drupal 8中,你应该使用\Drupal::moduleHandler()->invokeAll('help')。在这个例子中\Drupal::moduleHandler()通过服务容器找出已注册的模块处理程序服务,然后调用服务的invokeAll()方法。

这种机制比Drupal 7的好,因为它允许Drupal 发行版、托管商或者其他模块通过修改注册的模块处理服务类,实现ModuleHandlerInterface接口,来覆写模块运行其他模块钩子的方式。这种修改对于其他部分的Drupal代码而言是透明的。这意味着Drupal的更多部分可以在不修改核心的情况下进行替换。代码的依赖也有更好的文档并且有关的边界也更好的分离。最好,服务可以通过接口进行更加简便的单元测试,而不是整合测试。

对比Drupal 7全局变量与Drupal 8服务

Drupal7的一些全局变量,如 global $language 与 global $user 在Drupal 8中也是通过服务进行访问(并且不再是全局变量)。请参考Drupal::languageManager()->getLanguage(Language::TYPE_INTERFACE) 与 Drupal::currentUser()

更改已有服务,提供动态服务

原文地址:https://www.drupal.org/node/2026959

服务容器具有多个优点,因为每个服务都可以通过一个字符串键来访问/实例化,并且拥有一个已定义的接口,它可以用其他实现来替换。如果需要修改一个已有的服务,只需要实现一个ServiceProviderBase的子类并且编写alter()方法。

如:在my_module中定义my_module/src/MyModuleServiceProvider.php

<?php
/**
* @file
* Contains Drupal\my_module\MyModuleServiceProvider
*/
namespace Drupal\my_module;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
/**
* Modifies the language manager service.
*/
class MyModuleServiceProvider extends ServiceProviderBase {
  /**
   * {@inheritdoc}
   */
  public function alter(ContainerBuilder $container) {
    // Overrides language_manager class to test domain language negotiation.
    $definition = $container->getDefinition('language_manager');
    $definition->setClass('Drupal\language_test\LanguageTestManager');
  }
}
?>

需要注意,如果你想系统自动识别你对服务的修改,这个类的名称必须是你模块机读名称的驼峰版本,并且需要在模块的顶层命名空间下(Drupal\your_module_name),并且需要实现\Drupal\Core\DependencyInjection\ServiceModifierInterface (跟 ServiceProviderBase 一样)。

另一个可能比较实用的服务替换例子是字符串翻译(t()功能对应的服务)。Drupal核心没有提供区域语言或者非正式/正式语言,而它们可以提供最小的翻译差异。通过替换字符串翻译服务,可以很容易的在第三方模块中解决此问题。

最后,还可以通过定义register()方法来动态注册服务,虽然很少会这样做。

在表单上应用依赖注入

原文链接:https://www.drupal.org/node/2203931

依赖于Drupal服务或者自定义服务的表单应该通过依赖注入来访问该服务。

如一个表单(与Form API in Drupal 8 中的类似)使用'current_user'服务来获取当前用户的uid。假设模块名的modules/example,那么文件 /modules/example/lib/Drupal/example/Form/ExampleForm.php 内容:

<?php
/**
* @file
* Contains \Drupal\example\Form\ExampleForm.
*/
namespace Drupal\example\Form;
use Drupal\Core\Form\FormBase;
/**
* Implements an example form.
*/
class ExampleForm extends FormBase {
  /**
   * @var AccountInterface $account
   */
  protected $account;
  /**
   * Class constructor.
   */
  public function __construct(AccountInterface $account) {
    $this->account = $account;
  }
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    // Instantiates this form class.
    return new static(
      // Load the service required to construct this class.
      $container->get('current_user')
    );
  }
  /**
   * {@inheritdoc}.
   */
  public function getFormID() {
    return 'example_form';
  }
  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, array &$form_state) {
    // Get current user data.
    $uid = $this->account->id();
    // ...
  }
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, array &$form_state) {
    // ...
  }
}
?>

 

create方法是一个返回ExampleFormObject新实例的工厂方法,加载一个或者多个服务。这些服务可以是任何核心服务,在core.services.yml 或者任何 *.services.yml 文件中定义。

ExampleForm::__construct 使用create方法中加载的服务并且保存在类的变量中。ExampleForm::create中加载服务的顺序需要与ExampleForm::__construct中的参数顺序一致。

create方法位于Drupal\Core\DependencyInjection\ContainerInjectionInterface中,允许控制器使用服务来初始化。Drupal\Core\Form\BaseForm已经实现了此接口。其他继承自Drupal\Core\Form\BaseForm的表单可以使用依赖注入,如ConfigFormBase 与 ConfirmFormBase。

服务标签(Service Tags)

原文:https://www.drupal.org/node/2239393

标签用来表面一个服务应该用一种特别的方式来注册或者使用,请参考Symfony关于服务标签的文档
 
 
下面是D8中使用的服务标签列表。虽然这个列表中的大部分都还没有文档,但可以作为模块开发中的有用参考。
 
Name Provided By Description
access_check Drupal Add a access checker for a route. THIS IS LIKELY TO CHANGE.
authentication_provider @todo @todo
breadcrumb_builder @todo @todo
cache.bin @todo @todo
cache.context @todo @todo
config.factory.override @todo @todo
encoder @todo @todo
entity_resolver @todo @todo
event_subscriber @todo @todo
needs_destruction @todo @todo
normalizer @todo @todo
paramconverter @todo @todo
path_processor_inbound @todo @todo
persist @todo @todo
plugin_manager_cache_clear @todo @todo
route_enhancer Symfony available here
route_filter @todo @todo
route_processor_outbound @todo @todo
string_translator @todo @todo
theme_negotiator Drupal Change the way that drupal chooses the default theme. Seehttps://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Theme%21T...
twig.extension Twig available here

 

 

Drupal8中的向导API

原文链接Tour API in Drupal8

您可以通过将YAML文件添加到您的模块配置文件夹的方式来创建若干个向导团,从而让它们和Drupal8向导模块一起配合使用。

或者您也可以通过调用向导用户界面模块(Tour UI)来创建向导团。

论坛(模块)向导样例

让我们先来参考一个例子,这个例子在《为论坛模块编写向导集成》当中有提到。

YAML文档的命名

用于实现论坛的添加/编辑页面的YAML文件的命名类似于tour.tour.forum-container.yml,也就是说它遵循module.type.id.yml模式。

如果你想在你的模块当中为你的‘configure pants’(“配置裤”)表单提供一个建议,你可以把你的模块命名为tour.tour.configure-pants.yml。

向导YAML文档的内容

用来添加或编辑论坛页面的YAML文档内容如下:

id: forum-container
module: forum
label: Add or edit a forum container
langcode: en
uuid: dd06919e-9c54-49a8-b0e7-bbf7e03d0db7
routes:
  - route_name: forum.add_container
  - route_name: forum.add_forum
tips:
  introduction:
    id: introduction
    plugin: text
    label: Adding or editing a container
    body: This form can be used to edit an existing container or add a new container to your site.Containers are used to group forums together. For example if you ran a Drupal forum you might create a 'Support' container and include forums for 'Installing Drupal' and 'Getting started' inside the 'Support' Container
    weight: "1"
  container-name:
    id: container-name
    plugin: text
    label: Container name
    body: Enter a name to identify this container. Eg 'Support'
    weight: "2"
    attributes:
      data-id: edit-name
  container-description:
    id: container-description
    plugin: text
    label: Container description
    body: Give your container a description to help identify the purpose of the container and the types of forum it will contain. You can also use the container description to provide guidelines for other site administrators to help them decide which container a new forum might belong in.
    weight: "3"
    attributes:
      data-id: edit-description
  container-save:
    id: container-save
    plugin: text
    location: top
    label: Save
    body: When you have finished completing the form, click 'Save' to create the new container or save the changes to an existing container.
    weight: "6"
    attributes:
      data-id: edit-submit

YAML文档的相关属性

  • id:向导的ID号。
  • module:你的模块名(在我们的案例中叫“forum”)。
  • langcode:向导的语言。我们现在有配置架构,这样配置过的实体最终将能够被翻译。
  • label:向导的名称,在核心中不会用到它们,但是将被向导界面模块(正在积极开发中)所利用。
  • routes:一个用于活跃向导的路径数组。把它们与route_name和可选择的rout_params(也是一个数组)一起指定为一个数组。路径名可以在每一个模块的routing.yml文件中找到。
  • tips:一个用来构建向导,与提示插件配置有关的数组

提示插件

提示类型对核心插件系统产生影响并且实现了一个TipPluginInterface(注:提示插件接口)。核心插件系统拥有一个“文本”类型的插件,同时在测试的时候还用到了“图片”插件。实现了这个API,任何模块都可以创建一个新的插件。比如,你想在你的建议当中用到Youtube视频或者其他更为丰富的交互。只要实现了这个API,那么这(个功能)就特别容易实现。

插件配置

在论坛样例的YAML文档中,上述每一个建议的配置如下:

 

container-delete:
    id: container-delete
    plugin: text
    label: Delete
    body: Use this button to delete your container. You will be required to confirm you wish to delete the container before it is actually deleted.
    weight: "7"
    attributes:
      data-id: edit-delete

这个配置为论坛的编辑页面的删除按钮定义了一个建议,这些键都不言自明:

  • id:建议的ID号
  • plugin:插件类型(在核心中只有文本插件是可用的)
  • label:建议的头部,在建议中,它们被h3标签渲染
  • body:建议的主体,允许使用标记
  • location:箭头的位置
  • weight:在向导里面,提示按权重排列。
  • attributes:属性被传递给建议的呈现函数,但是有几个特别有意思的地方值得注意——控制建议(展现)的位置。
  • data-id:命名提示目标的dom元素的ID号。例如,在删除按钮的样例当中,它的目标元素ID号是“edit-delete”,无需在前面再加入#号,内部的joyride插件取得元素的ID并把它直接传递给(对应的)文档。getElementById()(返回的)就是一个ID字段,而不是一个jquery选择器。
  • data-class:如果你的目标元素没有ID号,你可以用data-class来标记它。注意它的前面不需要用到句点(即英文的句号)。Joyride插件会把它解析成一个jquery选择器,因此你可以使用更复杂的选择器规则。提供一个以class名开头没有句点的选择器。例如
action-links a[href=”/admin/structure/forum/add/forum”]

它会被joyride插件解析为“.action-links a[href=”/admin/structure/forum/add/forum”]”。

  • 如果你同时省略了.data-id和.data-class,那么建议将以模态化的方式显示,而不是被定位到某个元素,这对于一般的单个页面/表格的介绍等是非常有用的。

样式和标准

请确保你对向导团和UI文本的一般接口模式和样式指南有所了解。

编写/开发向导团

如果你正在编写一个向导,你需要遵循以下步骤,以查看向导在网站中出现的实际位置。

如果你只是测试一个向导补丁,请确认您在应用补丁后启用了对应的模块。

  • 创建你的向导yml文件,并命名成tour.tour.[tour-name].yml。例如:
tour.tour.language.yml
  • 如果你想使用你的样例yml文件,试着用以下方式启动
core/modules/view_ui/config/tour.tour.views-ui.yml

复制yml文件到网站的配置目录下的活跃和暂存的子目录,比如:

sites/default/files/config_zAYjIjYlwIIJYm5UOQscPNMnQQDeWd96fKV9ThwO9XU/staging
sites/default/files/config_zAYjIjYlwIIJYm5UOQscPNMnQQDeWd96fKV9ThwO9XU/active

(点此查看)更过关于Drupal8管理配置

  • 进入网站的后台管理部分,进入到admin/config/development/configuration页面,然后点击“导入所有”。
  • 把下面这行代码加入到你的settings.php文件
$settings['cache']['config'] = 'cache.backend.memory';

它将禁用配置文件的缓存(不推荐在生产网站上使用)。这样,你就做了一些变化,重新加载网页来查看这些变化(否则每一次你都要清除缓存,那是一件相当乏味的事情)。

  • 你每一次确实需要从向导最开始的地方开始,直到补丁程序被加载进去。

#1942576: Tour tips to be able to link to other pages and start tour's automatically.

  • 一旦向导准备活动,你需要将该文件复制到正确的位置。例如,一个语言模块的向导(文件)不会存放在
core/modules/language/config

然后像往常一样创建你的补丁程序。

获取更多更高级的功能

如果你发现你需要做一些比显示文本提示更高级的(功能),例如,你需要一些额外的参数或逻辑,你将需要通过调用TipPluginInterface(接口)来创建一个提示插件。以tour_test模块为例,它包含了一个代替文本来展示图片的图片插件,自带了一个“url”配置键,而不是“主体”。

此页面是从这篇博文改编

你可以在YouTube上观看完整的过程