drupal8模块开发

2025-04-27

Drupal 8模块开发

namespace Drupal\\field_example\\Controller;

use Drupal\\Core\\Controller\\ControllerBase; use Drupal\\Core\\Entity\\EntityManagerInterface; use Drupal\\Core\\Entity\\Query\\QueryFactory;

use Symfony\\Component\\DependencyInjection\\ContainerInterface; /**

* Controller routines for field example routes. */

class FieldExampleDefaultController extends ControllerBase {

/**

* @var \\Drupal\\Core\\Entity\\EntityManagerInterface */

protected $entityManager;

/**

* @var \\Drupal\\Core\\Entity\\Query\\QueryFactory */

protected $queryFactory;

/**

* @param \\Drupal\\Core\\Entity\\EntityManagerInterface $entityManager * @param \\Drupal\\Core\\Entity\\Query\\QueryFactory $queryFactory */

public function __construct(EntityManagerInterface $entityManager, QueryFactory $queryFactory) {

$this->entityManager = $entityManager; $this->queryFactory = $queryFactory; }

/**

* @param \\Symfony\\Component\\DependencyInjection\\ContainerInterface $container

* @return static */

public static function create(ContainerInterface $container) { return new static(

$container->get('entity.manager'), $container->get('entity.query') ); }

46

Drupal 8模块开发

public function simple() { /**

* We need to get one node with a body field. */

/**

* @var $query \\Drupal\\Core\\Entity\\Query\\QueryInterface */

$query = $this->queryFactory->get('node');

// We just want some nodes. $nids = $query->execute();

// If there are no nodes, we exit. if (count($nids) < 1) {

return ['#markup' => $this->t('No nodes with a body field.')]; }

/**

* @var $entity_storage \\Drupal\\Core\\Entity\\ContentEntityStorageBase */

$entity_storage = $this->entityManager->getStorage('node');

/**

* @var $entity \\Drupal\\Core\\Entity\\Entity */

$entity = $entity_storage->load(array_values($nids)[0]);

// We will get the values from here.

return [

'#markup' => 'placeholder', ]; } }

创建节点后,我们来取得 body 字段的值,并使用之前讲到的方式改变它。 /**

* @var $field \\Drupal\\Core\\Field\\FieldItemList */

$body_field = $entity->get('body');

// If we want a value off the first item, we can use a magic method __get()

47

Drupal 8模块开发

// Which is sometimes easier to use. $simple_value = $body_field->value;

// We get a array of items for this field.

// Unlike Drupal 7, there are no language keys. $field_value = $body_field->getValue();

// We will set the summary to the value.

// We don't need to update the entity because the field setValue does that already. $body_field->summary = $body_field->value;

// or

$field_value[0]['summary'] = $field_value[0]['value']; $body_field->setValue($field_value); /**

* @var $definition \\Drupal\\Core\\Field\\BaseFieldDefinition */

$definition = $body_field->getFieldDefinition();

$field_type = $definition->get('field_type');

return [

'field_type' => [

'#markup' => 'Field Type: ' . $field_type, '#theme_wrappers' => [ 'container' => [], ], ],

'field_value' => [

'#markup' => 'Field Value: ' . $body_field->value, '#theme_wrappers' => [ 'container' => [], ], ], ];

48

Drupal 8模块开发

第八章 服务,依赖注入和服务容器

8.1 :服务

本课?

创建模块 examples/service_example,使用自定义服务(service )和依赖注入(dependency injection) 学习服务及他们与依赖注入的关系 研究依赖注入的含义及使用 研究服务容器的含义及使用

专注“服务在编程中意味着什么、为什么他们比较实用” 研究创建和注册一个简单的服务

我们将创建一个定义 service_example.example_service 服务的模块,之后在页面 examples/service-example/simple 中使用它,页面由一个简单的 ServiceExampleController 控制器控制。

创建基础模块

我们将创建以下文件。之前课程里已经解释如何创建路由和 info 文件。 service_example.info.yml 文件: name: 'Service example' type: module

description: 'An example module showing how to create and use a service.' package: 'Acquia Example modules' core: 8.x

service_example.routing.yml 文件: service_example_simple:

path: 'examples/service-example/simple' defaults: _controller: '\\Drupal\\service_example\\Controller\\ServiceExampleController::simple_example' requirements:

_access: 'TRUE'

49

Drupal 8模块开发

创建一个服务

我们将创建一个简单服务,它提供一个只返回字符串的功能。

之后课程里我们将学习更多服务相关知识,暂时你可以把它想象为全局函数或者库。

要创建一个服务,你需要使用 service_example.services.yml 文件让系统知道它的存在。之后在 src/ServiceExampleService.php 文件内追加类定义功能。

service_example.services.yml 文件里,顶级是一个服务列表。每个服务用模块名作为前缀 module_name.service_name 。服务的类名会被定义,类名和包含这个类的文件同名。

service_example.services.yml 文件 : services:

service_example.example_service:

class: Drupal\\service_example\\ServiceExampleService

ServiceExampleService.php 文件看起来很类似控制器。我们描述定义什么样的类,定义名字空间(Drupal\\module_name),我们创建一个公有函数和一些内部逻辑。这个服务的内部逻辑是:当服务被创建时,一个变量被设置为 Student 。当其他代码使用这个服务时,他们能够通过调用 getServiceExampleValue() 函数返回字符串 Student 。 下载文件

* @file

* Contains \\Drupal\\service_example\\ServiceExampleService. */

namespace Drupal\\service_example;

class ServiceExampleService {

protected $service_example_value;

/**

* When the service is created, set a value for the example variable. */

public function __construct() {

$this->service_example_value = 'Student'; }

/**

* Return the value of the example variable. */

public function getServiceExampleValue() {

50

Drupal 8模块开发

目录

第一章 Examples 模块,Symfony,控制器和菜单 ................................................................ 3

1.1 :Examples 模块,Symfony,控制器 ............................................................................. 3 1.2 :菜单................................................................................................................................ 5 1.3 :实验室和其他信息 ........................................................................................................ 9 第二章 区块,配置和表单 ..................................................................................................... 11

2.1 :区块与配置 .................................................................................................................. 11 2.2 :创建表单 ...................................................................................................................... 13 2.3 :实验室和其他信息 ...................................................................................................... 18 第三章 表单配置和管理 ......................................................................................................... 19

3.1 :配置表单和管理 .......................................................................................................... 19 3.2 :配置和 Config 对象 ................................................................................................... 22 3.3 :实验室和其他信息 ...................................................................................................... 26 第四章 实体,内容实体和配置实体 ..................................................................................... 27

4.1 :实体,内容实体和配置实体 ...................................................................................... 27 4.2 :实验室和其他信息 ...................................................................................................... 29 第五章 实体字段 ..................................................................................................................... 30

5.1 :实体字段 ...................................................................................................................... 30 5.2 :导出配置 ...................................................................................................................... 32 5.3 :实验室和其他信息 ...................................................................................................... 34 第六章 实体查询和实体加载 ................................................................................................. 35

6.1 :实体查询和加载 .......................................................................................................... 35 6.2 :加载实体 ...................................................................................................................... 38 6.3 :实验室和其他信息 ...................................................................................................... 42 第七章 加载和编辑字段 ......................................................................................................... 43

7.1 :加载和编辑字段 .......................................................................................................... 43 7.2 :加载字段的例子 .......................................................................................................... 45 第八章 服务,依赖注入和服务容器 ..................................................................................... 49

8.1 :服务.............................................................................................................................. 49 8.2 :服务和控制器 .............................................................................................................. 51 8.3 :依赖注入 ...................................................................................................................... 54 8.4 :概要.............................................................................................................................. 57 第九章 创建元素,主题化元素和管理前端库 ..................................................................... 58

9.1 :创建元素,主题化元素和管理前端库 ...................................................................... 58 9.2 : Twig ............................................................................................................................ 63 9.3 :向组件添加 Javascript/CSS/库 ................................................................................... 65 9.4 :高级渲染数组模式 ...................................................................................................... 69 9.5 :模式概述 ...................................................................................................................... 72 9.6 :实验室.......................................................................................................................... 73

1

Drupal 8模块开发

第十章 单体和功能测试 ......................................................................................................... 74

10.1 :单体和功能测试 ........................................................................................................ 74 10.2 :单体测试 .................................................................................................................... 76 10.3 :功能测试 .................................................................................................................... 80 10.4 : PHPUnit Doubles / Mocks / Stubs ............................................................................ 85 第十一章依赖注入回顾 ................................................................................................................. 87

11.1 :依赖注入回顾 ............................................................................................................ 87 11.2 :依赖注入和服务 ........................................................................................................ 89 11.3 :依赖注入和控制器 .................................................................................................... 91 11.4 :依赖注入和插件 ........................................................................................................ 93 11.5 : PhpStorm 策略........................................................................................................ 96

2

Drupal 8模块开发

第一章 Examples 模块,Symfony,控制器

和菜单

1.1:Examples 模块,Symfony,控制器

Drupal 8 和 Examples 模块

在探索 Drupal 8 的各种模块开发能力时我们会用到Examples项目,主要利用其中的 page_example 模块。

确保下载 8.x 版本,解压备用。

Symfony

什么是 Symfony ?

Symfony 是一个 PHP 框架,基于其最佳 WEB 开发实践,帮助快速开发 PHP 应用。

Drupal 借助 Symfony 的代码构建路由和控制器,当然这些代码并不是专门为 Drupal 开发, 而是按照通用标准实现的。

代替 Drupal 7 的过程式编程 Drupal 8 实现了面向对象架构,更具灵活性。 Drupal 8 的灵活性增强,部分得益于把 Symfony 框架作为其核心的一部分。 Symfony 有几十个独立的库,这些库可以应用于任何 PHP 项目。 注意:

Drupal 8 并未使用 MVC 架构模式,它只实现了控制器编程模型。

为什么使用 Symfony ?

Symfony 引入的一个最有用的工具是能够根据位置自动加载代码。

现在 Drupal 开发者无需再创建一个完整的 include 文件,只一个 use 行便可包含可重用代码。

Drupal 是一个 PHP 应用,Symfony 是有可用库的 PHP 框架,在 Drupal 核心内使用 Symfony 既有益处也有风险。

框架的改变可能会增加有经验创造者的数量,尤其是那些 PHP 开发者,他们可以很容易的过渡到 Drupal ,为此积极学习、利用这个项目。但对于 Drupal 社区内精通 Drupal 而对现代化的 PHP 技术欠缺的这些人可能会形成门槛。

理想情况下,通过创建有用的学习工具和彼此分享培训信息会解决这些风险,为之前社区内的 Drupal 开发者降低门槛。

什么是控制器?

对于习惯了之前 Drupal 7 那种过程式开发(虽然部分内容使用了 OOP 方法)的人来说,控制器这个概念可能略有挑战性。

一个控制器就是一个 PHP 函数,用于处理 HTTP 请求(HTTP request),构造并返回一个 HTTP 响应

(HTTP response,比如 Symfony Responseobject)。响应可以是一个 HTML 页面,一个 XML

3

Drupal 8模块开发

文档,

一个序列化的 JSON 数组,一个图片,一个重定向,一个 404 错误或者任何你能想到的东西。

控制器可以包含呈现页面内容的任何逻辑。

控制器等价于 Drupal 7 中hook_menu() 内的页面回调函数。你创建一个控制器类,

定义一个方法显示页面数据。同样的,在 Drupal 7 中你会创建一个被 hook_menu() 中的 \引用的函数,显示这个页面数据。

什么是路由,路由器?

Symfony 路由器允许你定义创造性地 URL,映射到应用的不同区域。一个路由是从一个 URL 路径到一个控制器的映射。

怎样创建第一个 Drupal 8 模块?

作为起点,我们基于 Examples 创建一个简单模块,现在大多数模块结构基于YAML。 YAML 是一个人类友好的数据序列化标准,可以由任何编程语言实现。

创建目录

创建 page_example.info.yml 文件 创建 page_example.routing.yml 文件 创建 PageExampleController.php 文件 创建 page_example.links.menu.yml 文件 重构缓存 测试

设置目录

首先我们必须创建文件和目录结构,使 Drupal 8 意识到这个模块。

Drupal 7 最少需要有.info和.module文件。Drupal 8 需要的更少,只需要有 YAML 版本的 .info 文件,叫做.info.yml,它里面包含 Drupal 8 能够识别的数据结构。

一个主要的变化是自定义和捐献模块被放在了 Drupal 根下(modules/[directory])。 所有核心代码被移到了一个单独(core/)的目录里。

在模块(modules/)目录,还是像 Drupal 7 那样被推荐把自定义和捐献模块分开放置。

在 Drupal 8 的文档根目录,完成以下几步:

导航到/modules目录 创建目录examples 进入这个新目录

cd examples

创建目录page_example,这个将对应 Examples 项目,模块名叫 page_example。 进入新目录

cd page_examples

本课我们不会安装实际的 Examples 模块,而是使用它的代码片断创建一个简单的“Hello world”模块,目的在于向大家一步步地展示构建模块的过程。

4

Drupal 8模块开发

创建 info.yml 文件

现在你已经在正确的目录位置,是时候创建 .info 文件了。

创建page_example.info.yml 文件内增加以下代码

name: 'Page example' type: module

description: 'An example module showing how to define a page to be displayed at a given URL.' package: 'Example modules' core: 8.x

type 是需要的新属性,可以设置为 module 或 theme (主题也需要*.info.yml文件)。 如果你对 Drupal 7 熟悉,你应该知道其他属性的含义。

你可以查看Drupal 8 documentation page 了解更多可以在 .info.yml 使用的 key|value,以及相关的变更记录。

现在去 Drupal 站点的 Extend 页面,激活“Page example”模块。

在激活模块之前不需要创建 .module 文件,我们希望最终的*.module文件尽可能的小,功能性代码应该尽可能的放置在 service、控制器和插件等面向对象结构内。

创建 routing.yml

现在我们将为新模块创建菜单。

1.2 :菜单

hook_menu() 和它的回调函数发生了什么?

Drupal 8 里,hook_menu() 系统被拆分为几个 YAML 文件:

YAML 文件

modulename.routing.yml

描述

包含 URL 路径和回调函数的映射关系。

modulename.links.menu.yml 包含菜单项的结构。

modulename.links.action.yml 等效 Drupal 7 的常量 MENU_LOCAL_ACTION 。 modulename.links.task.yml

等效 Drupal 7 MENU_DEFAULT_LOCAL_TASK 。

Drupal 8 中我们使用Symfony2 components 处理路径到控制器方法的路由,进行数据显示。

在同一个目录((page_example),创建 page_example.routing.yml 文件。 添加以下代码

page_example_description:

path: '/examples/page_example' defaults:

_controller: '\\Drupal\\page_example\\Controller\\PageExampleController::description' requirements:

_access: 'TRUE'

page_example_simple:

5

Drupal 8模块开发

protected function intermediateQuery() {

$query = $this->entity_query->get('node') ->condition('status', 1)

->condition('changed', REQUEST_TIME, '<')

->condition('title', 'ipsum lorem', 'CONTAINS') ->condition('field_tags.entity.name', 'test'); $nids = $query->execute(); return array_values($nids); }

public function conditionalQuery() { return [

'#title' => 'Published Nodes Called \lorem\That Have a Tag \

'content' => [

'#theme' => 'item_list',

'#items' => $this->intermediateQuery() ] ]; }

protected function advancedQuery() {

$query = $this->entity_query->get('node') ->condition('status', 1)

->condition('changed', REQUEST_TIME, '<'); $group = $query->orConditionGroup()

->condition('title', 'ipsum lorem', 'CONTAINS') ->condition('field_tags.entity.name', 'test'); $nids = $query->condition($group)->execute(); return array_values($nids); }

public function conditionalGroupQuery() { return [

'#title' => 'Published Nodes That Are Called \lorem\Or Have a Tag \

'content' => [

'#theme' => 'item_list',

'#items' => $this->advancedQuery() ] ]; }

41

Drupal 8模块开发

下一步编辑既存的路由文件,调用公有方法: 下载文件

query_example.simple:

path: 'examples/query_example/simple' defaults: _controller: '\\Drupal\\query_example\\Controller\\QueryExampleController::basicQuery' requirements:

_access: 'TRUE'

query_example.intermediate:

path: 'examples/query_example/intermediate' defaults: _controller: '\\Drupal\\query_example\\Controller\\QueryExampleController::conditionalQuery' requirements:

_access: 'TRUE' query_example.advanced:

path: 'examples/query_example/advanced' defaults: _controller: '\\Drupal\\query_example\\Controller\\QueryExampleController::conditionalGroupQuery'

requirements:

_access: 'TRUE'

6.3 :实验室和其他信息

总结

本课我们看到了在 Drupal 8 中如何查询和加载实体。Drupal 8 把 7 中的

EntityFieldQuery 类进行了大修,提供了查询内容实体和配置实体的健壮 API。我们只看了查询内容实体,但查询配置实体用法是相同的。

我们也看到了怎样通过查询实体得到的 ID 加载实体的方法。接下来我们会看下 Drupal 8 中如何定义内容实体类型,要复习下这个在 Drupal 7 是怎么做的,请查看 Sitepoint articles。

完整步骤:

在内容类型 Article 内追加 5 个节点(node),至少一个标题为”ipsum lorem“

拷贝课程 1 中的 page_example 模块,重命名为 query_example。 重命名相应的文件、术语、方法和类。 改变 PageExampleController 类

a. 创建 simpleQuery() 方法,返回 ID 列表。

b. 创建方法 basicQuery(),返回 simpleQuery() 产生的数组。

42

Drupal 8模块开发

c. 创建方法 intermediateQuery(),进行条件查询测试。 d. 创建方法 conditionalStaticQuery(),返回 intermediateQuery() 产生的数组。

e. 创建方法 advancedQuery(),进行 OR 条件测试。

f. 创建方法 conditionalGroupQuery(),返回 advancedQuery() 方法数组。 g. 编辑 YAML 路由文件,为上面 3 种查询追加响应路径。

你可以拷贝 QueryExampleController.php 代码,避免手动创建每个方法。

额外练习

创建个页面,使用 entity.manager 和 entity.query 服务显示 simpleQuery() 返回的那些节点的 teaser。

第七章 加载和编辑字段

7.1 :加载和编辑字段

Drupal 7 中的字段经常被存储为能够接纳多种模式的复杂数组。Drupal 8 中字段全部使用相同模式。不管字段是单值、多值、多语言、内置字段或者是附加字段,他们全部是类 FieldItemList 的对象。在某些方面,这使得开发 Drupal 8 容易,但也意味着原本在 Drupal 7 内比较简单的东西可能变得更复杂。

Drupal 7 里我们经常直接访问字段,使用钩子在他们加载时直接改变、保存他们。

Drupal 8 由关注字段改为关注实体,所以如果我们要改变字段首先需要通过实体得到他们。

下载隶属于实体的字段

要收集作为实体一部分的字段信息,使用以下代码: /**

* @var $entity \\Drupal\\Core\\Entity\\Entity */

$entity = $entity_storage->load($entity_id); /**

* @var $field \\Drupal\\Core\\Field\\FieldItemList */

$field = $entity->get($field_name);

43

Drupal 8模块开发

获取和设置简单字段

FieldItemList 类有 __get() 和 __set() 方法,可以处理多数情况。

这些方法假设你处理第一个项目(Item)。

// We get the string value from the field. $value = $field->value;

// Get the summary from a body field. $summary = $body_field->summary;

$field->value = 'changed value';

获取和设置复杂字段

这个例子演示了字段多余一个值的情况 —- 例如,body 字段有一个 summary。如果一个字段有多个值,你需要使用较长的 getValue() 模式访问它。 下载文件

// We get a complex field (like the body). $value = $field->getValue(); /**

* Value will look like this, * $value = [ * [

* 'value' => 'hello world', * 'summary' => 'the summary', * ], * [

* 'value' => 'foo', * 'summary' => 'bar', * ], * [

* 'value' => 'foo', * 'summary' => 'bar', * ], * ]; */

// We can access the value from before with, $string = $value[0]['value'];

$value[0]['value'] = 'changed value';

// Update the value

$field->setValue($value);

44

Drupal 8模块开发

获得字段信息

如果你想获取字段的信息(例如字段的类型),你需要先得到字段的定义。使用 getFieldDefinition ,字段定义是字段项目列表(the field item list)中的另一个对象。 /**

* @var $definition \\Drupal\\Core\\Field\\BaseFieldDefinition */

$definition = $field->getFieldDefinition();

$field_type = $definition->get('field_type');

7.2 :加载字段的例子

例子模块

我们将创建一个例子模块,加载一个节点(node),输出它的一些信息。 首先,至少创建一个节点,body 字段填写一些内容。 然后,我们创建这个模块,加载单一节点。

创建文件 field_example.info.yml : name: Field Example type: module

description: Example showing how to use fields core: 8.x

package: Examples

创建文件 field_example.routing.yml : field_example.simple:

path: 'examples/field-example/simple' defaults: _controller: '\\Drupal\\field_example\\Controller\\FieldExampleDefaultController::simple' requirements:

_access: 'TRUE'

创建控制器文件 src/Controller/FieldExampleDefaultController.php : 下载文件

* @file

* Contains \\Drupal\\field_example\\Controller\\FieldExampleDefaultController. */

45

Drupal 8模块开发

组装 examples/field_example 模块

学习 Drupal 7 和 Drupal 8 实体字段的不同 研究用模块创建自定义字段 研究用模块改变既存的字段

创建“初始导出配置”

在我们通过用户界面管理配置之前,需要把数据库中的默认配置导出到文件系统。 这允许我们更新存储的配置、在不同环境间迁移配置。

确认已更新 Drush 到最新版本后,运行以下指令: drush config-export

想了解更多 Drush 信息,请查看 Drush based workflow 。

创建一个新的内容类型

之前课程里已经讨论了如何创建内容类型,我们现在要创建一个。

确保 Link 模块激活

创建新内容类型 External,保存、编辑字段。

增加一个新的 Link 字段,命名为 URL、设置数量 1、保存。 重复使用 image 字段,命名为 Screenshot、保存。

导航到 /node/add ,你会看到新的内容类型 External 。

什么是 Configuration Manager ?

Configuration Manager 模块提供了一个用户界面,用于导入、导出配置变更,例如在站点的多个实例间分阶段配置数据。

查看配置变更

导航到配置管理部分 /admin/config/development/configuration,你会看到这些信息: Your current configuration has changed. Changes to these configuration items will be lost on the next synchronization:

* core.entity_form_display.node.external.default * core.entity_view_display.node.external.default * core.entity_view_display.node.external.teaser * core.extension

* field.field.node.external.body

* field.field.node.external.field_image * field.field.node.external.field_url * field.storage.node.field_url * node.type.external

* user.role.administrator 拷贝上面信息,下节会用到。

这些配置文件是什么?

配置文件也就是配置设置,默认存储在数据库内,也可以导出为配置文件。 我们将导出这些设置,在我们模块内使用。

31

Drupal 8模块开发

首先我们和相应的 UI 表单对比看下每个文件是什么:

node.type.external

内容类型编辑表单产生的设置。

路径: /admin/structure/types/manage/external core.entity_form_display.node.external.default 编辑模式下,定义字段表单组件如何表示的设置。

路径: /admin/structure/types/manage/external/form-display core.entity_view_display.node.external.default 默认查看模式下,定义字段如何表示的设置。

路径: /admin/structure/types/manage/external/display core.entity_view_display.node.external.teaser teaser 查看模式下,定义字段如何表示的设置。 field.field.node.external.body 定义字段 body 的设置。

field.field.node.external.field_image 定义字段 field_image 的设置。

路径:

/admin/structure/types/manage/external/fields/node.external.field_image

field.field.node.external.field_url 定义字段 field_url 的设置。

路径:/admin/structure/types/manage/external/fields/node.external.field_url

field.storage.node.field_url 定义字段 field_url 存储的设置。

路径: /admin/structure/types/manage/external/fields/node.external.field_url/storage

field.field 和 field.storage 文件有什么不同?

字段 API 定义了两个主要数据结构 FieldStorage 和 Field ,还有一个绑定概念。 FieldStorage 定义了一种特定的类型数据,隶属于实体。Field 隶属于单独的绑定。 一个绑定也可以理解为关联到一个可字段化实体类型的一组字段。

要了解更多关于绑定、字段和实体类型知识请查看 An Introduction to Entities。

5.2 :导出配置

导出配置

有三种方法可以导出配置文件。

完全导出 – 把站点的所有配置导出为一个压缩包

导航到 /admin/config/development/configuration/full/export,点击“导出”按钮,叫做 config-xxx.tar.gz 的压缩包会被下载下来。

部分导出 – 使用单独的导出配置,分别导出每个文件。

32

Drupal 8模块开发

导航到 /admin/config/development/configuration/single/export,使用“配置类型”和“配置名称”选择你要导出的配置。 Drush – 用 Drush 导出配置

上节我们已经使用 Drush 导出了配置。 导出结构类似这样:

sites/default/files/config_[your config path]/sync

drush config-export

导出后我们就可以把想要的配置文件拷贝到自己的模块内了。

创建空模块

创建空模块的步骤如下:

在 Drupal 8 的模块目录创建 fields_example 目录 创建子目录 config

在 config 内创建目录 install

从导出的配置内拷贝 node.type.external.yml 文件放在 config/install 目录下 在 fields_example 根下创建 external.info.yml 文件(模块名为:External Link Content Type)

结构类似这样:

是时候看下通过模块怎样创建内容类型了。

去 /admin/structure/types,删除我们之前创建的 External 内容类型。 去“扩展”激活 External Link Content Type 模块

去 /node/add/external,你会看到 External 内容类型的节点追加表单

现在我们有了一个提供 External 内容类型且能够正常运行的模块。去“扩展”卸载这个模块,为下一步做准备。

增加字段配置文件

把上一节我们导出的其他配置文件放置在fields_example/config/install/ 目录内。 应该有这些文件:

core.entity_form_display.node.external.default.yml core.entity_view_display.node.external.default.yml

33

Drupal 8模块开发

core.entity_view_display.node.external.teaser.yml field.field.node.external.body.yml

field.field.node.external.field_image.yml field.field.node.external.field_url.yml field.storage.node.field_url.yml

重新激活这个模块。去 /node/add/external 查看节点追加表单,body、url 和 screenshot 字段都会被表示出来。

5.3 :实验室和其他信息

新字段类型

Drupal 8 附带的几个新模块扩展了默认的字段 API。

Datetime – 不包含循环日期功能。

Email – 带有校验和 email 字段格式化器(转换纯文本 email 地址为可点击的 mailto)。

Entity reference – 链接到用户、术语和内容,一个自动完成字段。 Link – 包含基本的校验(只校验 http://)和几个常见选项(例如 nofollow 和在新窗口打开),以及不同的链接格式。

HTML 5 字段也支持 schema.org 标记(微数据)。核心没有用户界面,但你可以使用 RDF UI 模块把字段映射到 schema.org。

完整步骤

使用 Drupal 8 的用户界面创建内容类型 External 激活 Link 模块

向内容类型中追加 link 和 image 字段 导出站点配置

创建空模块 “External Link Content Type” 创建子目录 config,切换到子目录 创建目录 install

从导出的配置文件拷贝 node.type.external.yml 到 config/install 目录 创建文件 external.info.yml

删除第一步中你创建的 External 内容类型 激活 External Link Content Type 模块

确认通过 External Link Content Type 模块创建的 External 类型生效 卸载 External Link Content Type 模块 增加字段配置文件 重新激活模块

测试其他字段是否添加成功

额外问题和练习

使用 Configuration development 模块导出新字段配置。

解释下为什么模块需要 field.storage.node.field_url.yml 文件,而 body 和 image 字段不需要。

34

Drupal 8模块开发

第六章 实体查询和实体加载

6.1 :实体查询和加载

本课将解释在 Drupal 8 中如何查询实体。

本课?

组装模块 examples/query_example 学习实体查询 学习实体加载

研究、构建 PageExampleController 类

Drupal 历史上核心提供的操作和使用实体系统的方法比较有限,更丰富功能需要捐助模块来提供(Entity API)。Drupal 7 的核心提供了EntityFieldQuery 类,用于以编程的方式搜索实体。这个类包含了几个方法,帮助基于字段值或类属性进行实体查询。更多相关文档请查看 How to use EntityFieldQuery。

Drupal 7 之后,EntityFieldQuery被核心叫做 entity.query 的服务代替,它可以为特定的实体类型实例化一个查询对象。entity.query 是 QueryInterface 的一个实现。也可以通过 \\Drupal 名字空间或依赖注入静态的使用这个服务。

实体查询服务

这个查询服务允许我们取得匹配某种模式的一个节点列表。这类似一个 SQL 查询,但不是

35

Drupal 8模块开发

写一条语句,而是创建一个查询对象,之后执行它。我们能够调用这个查询对象的函数追加过滤或查询参数。

有两种方式创建查询对象。

基于类的方法(推荐)

之后章节中我们会讨论更多服务(services)内容。本例中,我们将使用类变量和函数获得一个查询对象,使用这个查询对象取得符合模式的节点 ID 列表。 下载文件

// This function and the next are part of the dependency injection pattern. // We will explain those in future chapters.

public function __construct(QueryFactory $entity_query) { $this->entity_query = $entity_query; }

public static function create(ContainerInterface $container) { return new static(

// User the $container to get a query factory object. // This object let's us create query objects. $container->get('entity.query') ); }

// This function shows how to public function myCallback() {

// Use the factory to create a query object for node entities. $query = $this->entity_query->get('node');

// Add a filter (published). $query->condition('status', 1);

// Run the query.

$nids = $query->execute(); }

静态方法(不推荐)

大多数代码不会存储在 *.module 文件内了,如果真存储在这里的话是不能使用服务容器的。静态方法同样适用于构建测试。这两种情况你可能需要静态调用 entity.query。 为了静态地使用 entity.query,添加以下内容到你的代码: $query = \\Drupal::entityQuery('node');

如果要使用不同的实体类型,替换 node。在 $query 变量里你可以指定任何其他实体类型的机器名以获得特定实体类型的查询对象。\\Drupal 名字空间的 entityQuery() 静态方法是使用 entity.query 服务的一个捷径。

查询实体

36

Drupal 8模块开发

下面是一个使用查询对象查询节点实体的例子。

这个基本查询返回站点的发布节点。$nids 变量返回节点 ID 的数组。 /**

* @var $query \\Drupal\\Core\\Entity\\Query\\QueryInterface */

$query = $this->entity_query->get('node') ->condition('status', 1);

$nids = $query->execute(); 注意:

象 PHPStorm 这样的 IDE 里,注释有自动完成功能。在我们的例子里,使用了一个变量声明的注解,

说明这个变量是 QueryInterface 类型以及这个类型所在的位置。

查询条件

下面的例子除了核实发布状态外还会使用属性和字段条件。这个查询里,我们返回所有被发布的节点 ID,他们的最后更新时间在这个查询运行之前。节点名必须包含“ipsum lorem”。节点的 field_tags 字段等于叫做“test”的分类术语。 $query = $this->entity_query->get('node') ->condition('status', 1)

->condition('changed', REQUEST_TIME, '<')

->condition('title', 'ipsum lorem', 'CONTAINS') ->condition('field_tags.entity.name', 'test');

$nids = $query->execute();

在 Drupal 7 里使用 EntityFieldQuery 的时候,propertyCondition 和 fieldCondition 是不同的,但现在 Drupal 8 没有区别。Drupal 8 的 QueryInterface 提供的 condition() 方法合并了属性和字段条件并且允许创建条件组。基于实体引用指定条件也是可以的,这要通过字段名追加 entity.(column) 来指定条件,象上面那个分类术语那样。

查询条件分组

这个例子演示怎样在复杂查询里创建 AND 或 OR 条件组。我们改变之前的查询,返回满足下面任意一个条件的节点:

标题包含“ipsum lorem”

field_tags 字段引用到“test”分类术语

$query = $this->entity_query->get('node') ->condition('status', 1)

->condition('changed', REQUEST_TIME, '<');

$group = $query->orConditionGroup()

->condition('title', 'ipsum lorem', 'CONTAINS') ->condition('field_tags.entity.name', 'test');

37

Drupal 8模块开发

$nids = $query->condition($group)->execute();

这个 OR 条件组使用 orConditionGroup 方法创建并作为条件传递给查询对象,你也可以使用 andConditionGroup 创建 AND 条件组。

查询修订版本 默认,实体查询只返回每个实体最新的修订版本,你可以使用 allRevisions() 获得所有修订版本。

$query->allRevisions();

6.2 :加载实体

加载实体

查询对象的 execute() 方法只是返回实体 ID 数组。要使用这些实体我们必须加载这些实体对象。Drupal 7 使用 entity_load() 函数返回一个实体对象数组(基于传递给它的 ID 数组)。Drupal 8 里这个函数变为只返回一个实体对象(当然参数也不再是 ID 数组,而只是一个 ID),而额外又提供了一个加载多个实体的方法: entity_load_multiple() 。

有三种方法加载实体:

类方法(推荐)

依赖注入将在之后章节讨论。代码中的重要部分是 example() 方法,它使用 entityManager 对象构建了一个节点存储对象。之后我们使用这个存储对象获得一个节点对象或者节点对象列表。 下载文件

protected $entityManager; /**

* {@inheritdoc} */

public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } /**

* {@inheritdoc} */

public static function create(ContainerInterface $container) { return new static(

$container->get('entity.manager') ); } /**

* Return a list of nodes that are published. */

38

Drupal 8模块开发

protected function example() {

// We get the node storage object.

$node_storage = $this->entityManager->getStorage('node');

// We use the load function to load a single node object. $nid = 1;

$node = $node_storage->load($nid);

// We load a revision $revision_id = 1;

$node = $node_storage->loadRevision($revision_id);

// We use the loadMultiple function to load an array of node objects keyed // by node ID. $nids = [1,2,3,4];

$nodes = $node_storage->loadMultiple($nids);

// We will discuss this in future lessons, but you can get the value of // simple fields using ->get('field_name')->value. return [

'#markup' => $node->get('title')->value, ]; }

静态方法(不推荐)

当容器无效时,你也可以使用静态方法。 // Get a node storage object.

$node_storage = \\Drupal::entityManager()->getStorage('node');

// Load a single node. $node_storage->load($nid); // Load multiple nodes.

$node_storage->loadMultiple($nids); 全局方法(不推荐)

全局方法类似 Drupal 7。 // Load a single entity.

$node = entity_load('node', $nid);

// Load multiple entities.

$nodes = entity_load_multiple('node', $nids); 创建控制器

我们把这些碎片汇总下。使用例子模块,在 QueryExampleController.php 的方法内追

加查询对象获得节点 ID。 下载文件

39

Drupal 8模块开发

/**

* @file

* Contains \\Drupal\\query_example\\Controller\\QueryExampleController. */

namespace Drupal\\query_example\\Controller;

use Drupal\\Core\\Entity\\Query\\QueryFactory;

use Symfony\\Component\\DependencyInjection\\ContainerInterface; use Drupal\\Core\\Controller\\ControllerBase; /**

* Controller routines for page example routes. */

class QueryExampleController extends ControllerBase {

protected $entity_query;

public function __construct(QueryFactory $entity_query) { $this->entity_query = $entity_query; }

public static function create(ContainerInterface $container) { return new static(

$container->get('entity.query') ); }

protected function simpleQuery() {

$query = $this->entity_query->get('node') ->condition('status', 1); $nids = $query->execute(); return array_values($nids); }

public function basicQuery() { return [

'#title' => 'Published Nodes', 'content' => [

'#theme' => 'item_list',

'#items' => $this->simpleQuery() ] ]; }

40


drupal8模块开发.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:雨污水管施工培训

相关阅读
本类排行
× 游客快捷下载通道(下载后可以自由复制和排版)

下载本文档需要支付 7

支付方式:

开通VIP包月会员 特价:29元/月

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:xuecool-com QQ:370150219