9.3. 创建一个自定义Drupal模块。以编程方式显示页面。
让我们以一些组织开始创建我们的模块。我们将继续区分自定义模块(custom)和贡献模块(contrib)。在Drupal中,模块位于 /modules 文件夹中。现在我们不再需要将它们放在 /sites/all/modules 中,尽管readme中提到这种方式仍然可以使用,但建议使用 /modules 文件夹。在 /modules 文件夹中,我们将创建两个子文件夹:custom 和 contrib。contrib 文件夹将存储来自 drupal.org 的第三方模块,而我们的自定义模块将放在 custom 文件夹中。
英文字幕:
代码示例可以在 GitHub 上查看:
https://github.com/levmyshkin/drupalbook8
将模块分成 contrib 和 custom 文件夹似乎是个小事,但请相信我,当你的网站上有200个贡献模块和30个自定义模块时,你会明白在网站上找到所有自定义模块和自定义代码有多么困难。如果你修改了某个插件模块中的代码(这种情况只有在绝对必要时才应进行),那么最好也将它移到 custom 文件夹中,这样在更新时就不会意外覆盖修改(或者其他开发者覆盖了你的更改,甚至你自己不经意地覆盖了他人的修改)。
好了,现在让我们创建我们的自定义模块。创建文件夹 /modules/custom/drupalbook。在此文件夹中,我们需要创建 drupalbook.info.yml 文件:
name: DrupalBook
description: Custom module for learning Drupal 8
type: module
core: 8.x
core_version_requirement: ^8 || ^9
package: DrupalBook
.info.yml 文件负责描述模块。文件中定义的信息将显示在“扩展(Extend)”页面上。文件名由模块名称 + .info.yml 组成。
Drupal 的 YML 文件是一种用于配置和设置的标准格式。YML 文件中的字段和值由冒号分隔,每一行都要用两个空格缩进。对于 YML 文件来说,格式非常重要,如果格式错误、空格过多或缺少冒号,Drupal 可能会报错或无法正确读取 YML 文件。
进入“扩展(Extend)”页面并启用你的模块:
现在我们已经启用了该模块,可以为其添加功能,使其在我们的网站上运行。首先,我们将在页面上显示纯文本。为此,我们需要创建另一个 YML 文件 drupalbook.routing.yml:
drupalbook.first_page:
path: '/first-page'
defaults:
_controller: '\Drupal\drupalbook\Controller\FirstPageController::content'
_title: 'Hello World!'
requirements:
_permission: 'access content'
让我们更详细地看看这个 YML 文件中的内容。
drupalbook.first_page: —— 这是我们路由的名称,Drupal 会通过它收集所有的路径列表。这个名称必须是唯一的,最好使用 module_name.route_name 的格式,因为一个模块中可能有多个路由,例如:drupalbook.first_page、drupalbook.second_page 等。
path: '/first-page' —— 路由的访问路径。请注意缩进必须是两个空格,如果你的文本编辑器在按 Tab 键时自动插入制表符,最好修改设置,使其插入两个空格。
defaults: —— 定义路由的默认设置以及如何创建该路由。实际上,创建路由有几种方法,其中一种是通过控制器(Controller)类,也可以通过其他类动态创建路由。我们稍后会介绍所有这些方法,但现在我们先通过 Controller 类学习一个简单的示例。Drupal 的路由系统来自 Symfony,并使用了 Symfony 的库。Symfony 是一个MVC框架,非常规范,也被用于其他项目,例如 Laravel。MVC代表模型(Model)、视图(View)和控制器(Controller)(这里的View不是Views模块,而是用于显示信息的模板;Model是用于与数据库交互的实体类,类似于Drupal中的Entity API)。在MVC中,控制器负责路由,这正是我们在本课中要做的。
_controller: '\Drupal\drupalbook\Controller\FirstPageController::content' —— 这个参数定义了哪个PHP类将显示我们的页面。在类名后用 :: 指定要调用的具体方法。大量的反斜杠是为了让Drupal能够理解你的PHP类位于哪个文件夹中。
_title: 'Hello World!' —— 页面标题。
_permission: 'access content' —— 查看此页面所需的权限,用户必须具有带有这些权限的角色才能访问此页面。
在添加路由的YML文件后,我们需要添加用于显示页面的PHP类。为此,我们首先需要了解模块中类文件的自动加载机制。因为Drupal中有很多类,而并不是所有类都需要在加载页面时全部加载,所以Drupal仅加载所需的类并与之交互。Drupal使用PSR-4标准的PHP自动加载机制:
https://www.php-fig.org/psr/psr-4/
让我们看一下我们在路由中指定的PHP类路径:
\Drupal\drupalbook\Controller\FirstPageController
\Drupal 表示该类通过Drupal库加载。Drupal现在本身也是一个大型的独立库,可以通过packagist引入:
https://packagist.org/packages/drupal/drupal
接下来,我们指定要使用的模块:
\drupalbook
之后,我们要么直接在模块文件夹中指定类名,要么指定更深的路径结构。但所有的类文件都必须放在 src 文件夹中。如果我们指定了 Controller 文件夹,它将位于:
/modules/custom/drupalbook/src/Controller
注意大小写,PHP对大小写敏感,所以 controller 和 Controller 是两个不同的文件夹。
最后,我们指定文件名,并需要为其添加 .php 后缀:
/modules/custom/drupalbook/src/Controller/FirstPageController.php
让我们创建此文件:
/**
* @return
* Contains \Drupal\drupalbook\Controller\FirstPageController.
*/
namespace Drupal\drupalbook\Controller;
/**
* 为DrupalBook模块提供路由响应。
*/
class FirstPageController {
/**
* 返回一个简单的页面。
*
* @return array
* 一个简单的可渲染数组。
*/
public function content() {
$element = array(
'#markup' => 'Hello World!',
);
return $element;
}
}
文件结构应如下所示:
现在我们需要清除缓存,以便新路由生效。然后访问页面 http://drupalbook/first-page,它就应该可以正常工作。
现在让我们看看用于显示此页面的PHP类:
/**
* @return
* Contains \Drupal\drupalbook\Controller\FirstPageController.
*/
这里我们指定的路径与routing YML文件中的相同,用于告诉Drupal从哪里加载该类。
namespace Drupal\drupalbook\Controller;
namespace 指明了类文件的路径。在从其他网站复制代码时要小心,当复制namespace时,请记得将模块名称更改为你自己的,我的是 drupalbook,你的可能不同。
class FirstPageController {
/**
* 返回一个简单的页面。
*
* @return array
* 一个简单的可渲染数组。
*/
public function content() {
$element = array(
'#markup' => 'Hello World!',
);
return $element;
}
}
这是我们用于显示页面的类。它包含一个 content() 方法,我们在 drupalbook.routing.yml 中指定了它。在这个方法中,我们返回一个带有 #markup 元素的数组,用于输出 HTML 内容。我们返回的是数组而不是字符串,因为通过常规路由不仅可以显示HTML,还可以输出API的JSON、XML或CSV文件。
模块的代码可以在GitHub上查看:
https://github.com/levmyshkin/drupalbook8
以上就是全部内容,在下一课中,我们将继续扩展模块的功能。