logo

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

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

演示 EBT 模块 下载 EBT 模块

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

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

演示 EPT 模块 滚动

滚动

在 Drupal 7 中操作数据库 - 第5课 - Extenders(扩展器)

14/10/2025, by Ivan

在 Drupal 7 中,SELECT 查询支持使用 扩展(Extenders)。扩展允许在运行时为查询对象添加额外功能,这些功能可以是新增的方法,也可以修改现有方法的行为。

在面向对象编程(OOP)中,这种机制通过设计模式实现,具体来说是采用了 装饰者模式(Decorator Pattern)。扩展通过为动态对象附加额外职责,为查询对象提供一种灵活的功能增强方式。

使用 Extenders(扩展)

要使用扩展,首先必须有一个查询对象。调用查询对象的 extend() 方法会返回一个新的查询对象,该对象包含扩展功能。例如:

<?php
$query = $query->extend('PagerDefault');
?>

在上面的示例中,创建了一个新的 PagerDefault 查询对象,它包含原始查询对象的全部内容并返回一个新的扩展对象。虽然可以直接使用 $query 而不添加扩展,但通过扩展可以获得更多的功能。请注意,extend() 返回的是一个新的对象,旧的 $query 不会自动更新。如果你不保存 extend() 的结果,就可能出现如下问题:

<?php
$query = db_select('node', 'n');
$query
  ->fields('n', array('nid', 'title'))
  ->extend('PagerDefault')   // 这一行返回一个新的 PagerDefault 对象。
  ->limit(5);               // 调用成功,因为 limit() 属于 PagerDefault 对象。

// extend() 的返回值没有保存,因此 $query 仍是原始的 Select 对象。
$query->orderBy('title');

// 这里执行的是原始 Select 对象,而不是扩展对象。
$result = $query->execute();
?>

为了避免混乱,推荐在定义查询对象时立即将扩展赋给变量:

<?php
$query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
$query->fields(...);
// ...
?>

这样 $query 变量将始终包含完整的扩展对象。请注意,多个扩展可以链式使用,但顺序可能会影响结果。例如,当查询同时需要分页和表格排序时,应先使用 PagerDefault,再使用 TableSort

创建自定义扩展

扩展其实就是一个实现了 SelectQueryInterface 的类,它的构造函数接收两个参数:一个 SELECT 查询对象和一个 DatabaseConnection 对象。扩展类需要实现接口中的方法,并通过构造函数传递底层查询对象。

在大多数情况下,扩展类会继承 SelectQueryExtender,该类已实现了接口中的主要逻辑。通常,我们只需要在自定义扩展类中添加或重写方法。类名即为调用 extend() 时的参数名称。

扩展类可以添加新方法或重写已有方法。未被重写的方法会自动传递给原始查询对象。当方法被重写时,扩展可以选择是否调用底层查询对象的方法,但必须返回与接口定义兼容的值(通常是查询对象自身或另一个扩展对象)。

以下示例展示了一个简单的扩展类:

<?php
class ExampleExtender extends SelectQueryExtender {

  /**
   * 重写 orderBy() 方法。
   */
  public function orderBy($field, $direction = 'ASC') {
    // 此处不执行任何排序,仅返回当前对象。
    return $this;
  }

  /**
   * 新增自定义排序方法。
   */
  public function orderByForReal($field, $direction = 'ASC') {
    $this->query->orderBy($field, $direction);
    return $this;
  }
}
?>

在此示例中,我们重写了原有的 orderBy() 方法,使其不执行任何操作;同时添加了 orderByForReal() 方法,用于真正执行升序排序。两个方法都返回查询对象本身,从而保持方法链的连续性。

任何模块都可以声明自己的扩展类。Drupal 核心自带两个常用扩展:

  • PagerDefault — 为查询添加分页功能。
  • TableSort — 为表格视图添加排序功能。

多数据库支持

扩展机制与 db_select() 类似,会根据数据库驱动查找带有驱动后缀的扩展类。例如,如果你的数据库使用 PostgreSQL,可以同时定义 ExampleExtender_pgsqlExampleExtender,Drupal 会自动选择可用的版本。