logo

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

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

演示 EBT 模块 下载 EBT 模块

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

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

演示 EPT 模块 滚动

滚动

9.3. 创建一个自定义Drupal模块。以编程方式显示页面。

17/10/2025, by Ivan

让我们以一些组织开始创建我们的模块。我们将继续区分自定义模块(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

好了,现在让我们创建我们的自定义模块。创建文件夹 /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)”页面并启用你的模块:

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;
  }
 
}

文件结构应如下所示:

contrib

现在我们需要清除缓存,以便新路由生效。然后访问页面 http://drupalbook/first-page,它就应该可以正常工作。

drupalbook

现在让我们看看用于显示此页面的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

以上就是全部内容,在下一课中,我们将继续扩展模块的功能。