在 Drupal 7 中操作数据库 - 第6课 - 动态修改查询(hook_query_alter)
动态选择查询(SELECT)的一个重要特性是,其他模块可以在运行时修改查询。这允许模块向查询中插入自己的指令,从而影响查询的行为,或在执行时进行修改,例如为节点(node)添加访问权限过滤。实现动态修改查询的机制包括三个部分:tagging(标签)、meta data(元数据) 和 hook_query_alter()。
Tagging(标签)
任何动态查询都可以添加一个或多个标签(tag)。这些标签用于标识查询类型,使其他模块能够识别并对其进行相应的处理。标签应为小写字母和数字的组合,遵循与 PHP 变量相同的命名规则。要为查询添加标签,请使用 addTag() 方法:
<?php $query->addTag('node_access'); ?>
可以使用以下三种方法来检测查询对象中是否存在标签:
<?php // 如果查询对象具有指定标签,则返回 TRUE。 $query->hasTag('example'); // 如果查询对象包含所有指定标签,则返回 TRUE。 $query->hasAllTags('example1', 'example2'); // 如果查询对象包含任意一个指定标签,则返回 TRUE。 $query->hasAnyTag('example1', 'example2'); ?>
hasAllTags() 和 hasAnyTag() 接受多个参数,参数顺序无关。使用标签非常简单,Drupal 核心和常用模块中已定义了一些标准标签,例如:
- node_access
- 该查询应考虑节点访问权限。
- translatable
- 该查询应包含可翻译字符串。
- term_access
- 该查询应考虑分类术语(taxonomy term)的访问控制。
- views
- 该查询由 Views 模块生成。
Meta data(元数据)
查询还可以附加元数据,用于在运行时提供额外信息。元数据可以是任何 PHP 数据类型,并使用字符串键进行标识:
<?php $node = node_load($nid); // 创建查询对象。 $query->addMetaData('node', $node); ?>
元数据本身不会直接影响查询执行,仅作为辅助信息在 hook_query_alter() 中被使用。可以通过 getMetaData() 方法获取已设置的元数据:
<?php $node = $query->getMetaData('node'); ?>
如果未设置对应键的元数据,则返回 NULL。
hook_query_alter()
标签(tags)和元数据(meta data)本身不会修改查询,而是为 hook_query_alter() 提供上下文信息。所有动态查询对象在执行 execute() 之前都会触发此钩子,使模块能够在查询编译之前对其进行修改。该钩子接收一个参数——查询对象。
<?php /** * 实现 hook_query_alter(). */ function example_query_alter(QueryAlterableInterface $query) { // ... } ?>
您还可以通过特定标签过滤,只处理带有指定标签的查询。例如,以下代码仅处理带有 node_access 标签的查询:
<?php function example_query_node_access_alter(QueryAlterableInterface $query) { // ... } ?>
关于 hook_query_alter() 有两个重要注意事项:
- 参数 $query 无需按引用传递。因为它是对象(在 PHP 5 及以上版本中,对象总是按引用传递),因此不需要使用 &。
- 参数类型明确指定为 QueryAlterableInterface。这可以防止传入错误类型的参数,同时确保在未来更好的兼容性。
hook_query_alter() 可以对查询对象执行任何操作(除了执行查询本身,否则可能导致无限循环)。该钩子可以利用标签和元数据来判断应采取的修改方式。开发者可以通过查询对象的方法添加字段、连接(join)、条件(condition)等,或控制访问权限等逻辑。以下代码示例展示如何通过引用修改查询结构:
<?php $fields =& $query->getFields(); $expressions =& $query->getExpressions(); $tables =& $query->getTables(); $order =& $query->getOrderBy(); $where =& $query->conditions(); $having =& $query->havingConditions(); ?>
请注意,这些方法返回的值均为引用,这意味着 alter 钩子直接操作的就是查询对象的内部结构。所有这些方法返回数组,其结构定义可参考 includes/database/select.inc 文件中关于 SelectQuery 的文档。