在 Drupal 7 中操作数据库 - 第5课 - Extenders(扩展器)
在 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_pgsql 和 ExampleExtender,Drupal 会自动选择可用的版本。