logo

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

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

演示 EBT 模块 下载 EBT 模块

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

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

演示 EPT 模块 滚动

滚动
03/10/2025, by Ivan

在 Drupal 中最常见的 SELECT 查询是使用数据库连接对象的 query() 方法的静态查询。
静态查询几乎是逐字传递到数据库的。

示例:

$database = \Drupal::database();
$query = $database->query("SELECT id, example FROM {mytable}");
$result = $query->fetchAll();

只有非常简单的 SELECT 查询才应该使用静态 query() 方法。如果您需要更复杂的查询、动态生成的查询或可变性,您应该使用 动态查询

不要使用此函数来执行简单的 INSERT、UPDATE 或 DELETE 查询。它们应该通过 insert()、update() 和 delete() 来处理。对于更复杂的多表 DELETE 查询,请参见 复杂 DELETE 查询

参数

数据库连接对象的 query() 方法接受三个参数:

  • $query:要执行的查询。如果需要,请使用占位符,并用大括号标记所有表名。
  • $args:替换查询中占位符的值数组。
  • $options:控制查询行为的选项数组(可选)。

表名前缀

在静态查询中,所有表名都必须用大括号 {...} 包围。

将表名放入大括号中会标记它们,以便数据库系统在必要时可以为其添加前缀字符串。前缀允许在一个数据库中运行多个站点,或者在某些情况下,在站点之间共享选定的表。它还能防止主机站点的数据泄露到测试中。

占位符

占位符标记了将字面量插入到执行查询中的位置。通过将它们与查询本身分开,我们允许数据库区分 SQL 语法和用户提供的值,从而避免 SQL 注入攻击。

$query = $database->query("SELECT id, example FROM {mytable} WHERE created > :created", [
  ':created' => REQUEST_TIME - 3600,
]);

上述代码将选择过去一小时(3600 秒)内创建的所有 mytable 的 id 和 example。占位符 :created 会在查询执行时动态替换为 REQUEST_TIME - 3600 的值。

查询可以有任意数量的占位符,但它们都必须有唯一的名称,即使它们的值相同。根据用例,占位符数组可以像上面那样内联指定,也可以预先构建并传递。数组顺序无关紧要。

以 "db_" 开头的占位符保留供系统内部使用,绝不应显式指定。

注意:无论类型如何,占位符都不应转义或加引号。因为它们是单独传递到数据库服务器的,服务器可以自行区分查询字符串和数值。

// 错误(在 :type 占位符外加引号)
$result = $database->query("SELECT example FROM {mytable} WHERE type = ':type'", [
  ':type' => 'mytype',
]);

// 正确(:type 占位符没有引号)
$result = $database->query("SELECT example FROM {mytable} WHERE type = :type", [
  ':type' => 'mytype',
]);

占位符不能用于列名和表名。如果它们来自不安全的输入,应通过 $database->escapeTable() 进行处理。

数组占位符

Drupal 数据库层包含额外的占位符功能。如果为占位符传递的值是数组,它会自动展开为逗号分隔的列表,匹配相应的占位符。这意味着开发者无需担心需要多少个占位符。

以下示例可以说明这种行为:

$result = $database->query("SELECT * FROM {mytable} WHERE id IN (:ids[])", [':ids[]' => [13, 42, 144]]);

以下两个语句等价于上面的语句:

$result = $database->query("SELECT * FROM {mytable} WHERE id IN (:ids_1, :ids_2, :ids_3)", [
  ':ids_1' => 13, 
  ':ids_2' => 42, 
  ':ids_3' => 144,
]);

$result = $database->query("SELECT * FROM {mytable} WHERE id IN (13, 42, 144)");

查询选项

数据库连接对象的 query() 方法的第三个参数是一个选项数组,用来定义查询的行为。通常大多数查询只会用到两个指令。其他值主要供内部使用。

键 "target" 指定要使用的目标。如果未指定,默认值为 "default"。目前唯一的另一个有效值是 "replica",表示查询应在从库服务器上执行(如果存在)。

键 "fetch" 指定如何获取该查询返回的记录。可接受的值有:PDO::FETCH_OBJ、PDO::FETCH_ASSOC、PDO::FETCH_NUM、PDO::FETCH_BOTH 或一个表示类名的字符串。如果指定了字符串,每条记录将作为该类的新对象提取。所有其他值的行为由 PDO 决定,并将分别以 stdClass 对象、关联数组、数值数组或带数字和关联键的数组形式提取记录。参见 http://php.net/manual/en/pdostatement.fetch.php。默认使用 PDO::FETCH_OBJ,为了保持一致性,除非有特殊原因,否则应使用该方式。

以下示例在可用时查询从库服务器,并将结果集的记录提取为关联数组。

$result = $database->query("SELECT id, example FROM {mytable}", [], [
  'target' => 'replica',
  'fetch' => PDO::FETCH_ASSOC,
]);

调用 query() 方法返回的结果对象可以用于获取每一行返回的数据,然后再访问列。在下面的示例中,变量 $result 包含查询返回的所有行,然后使用 fetchAssoc() 一次提取一行到变量 $row:

$sql = "SELECT name, quantity FROM goods WHERE vid = :vid";
$result = $database->query($sql, [':vid' => $vid]);
if ($result) {
  while ($row = $result->fetchAssoc()) {
    // 处理:
    // $row['name']
    // $row['quantity']
  }
}

复杂的 DELETE 查询

使用静态查询是一种简单紧凑的方式来表达包含多表删除的 DELETE 查询。

示例:

$database = \Drupal::database();
$database->query("DELETE {table1}, {table2} FROM {table1} INNER JOIN {table2} ON {table1}.id = {table2}.id WHERE {table1}.id=:recno", [":recno" => 2]);

(同时删除 table1 和 table2 中的行)