Hyperf中批量更新必须用where+update绑定参数防SQL注入,动态字段需白名单校验,多值更新用CASE WHEN绑定,事务内仍须参数化。

Hyperf 中用 QueryBuilder 安全地批量修改数据,核心是不拼接 SQL 字符串、不绕过参数绑定、不滥用 Db::raw()。只要坚持用 where + update 配合参数数组,就能天然防 SQL 注入,同时兼顾性能和可读性。
批量更新必须走 where + update 绑定参数
Hyperf 的 Db::update() 和 QueryBuilder 的 update() 方法底层调用 PDO 预处理,但前提是条件和值都通过参数传入。
- ✅ 正确(安全):
Db::table('users')->where('status', 0)->update(['status' => 1, 'updated_at' => now()]);
所有字段值和 where 条件均由框架自动绑定,无需手动传参数组 - ❌ 危险(SQL 注入):
Db::update("UPDATE users SET status = 1 WHERE id IN ({$idsStr})");
字符串拼接$idsStr,完全跳过预处理机制
带动态条件的批量更新要白名单校验字段名
如果 where 条件中的字段名来自用户输入(如前端传来的 filter_by),不能直接拼进查询链。
- ✅ 先校验再使用:
$field = in_array($request->input('filter_by'), ['status', 'role_id', 'type'], true) ? $request->input('filter_by') : 'status';<br>Db::table('users')->where($field, $request->input('value'))->update(['updated_by' => $uid]); - ❌ 禁止直接使用:
Db::table('users')->where($request->input('filter_by'), $val)->update(...);
未校验时,攻击者可传入filter_by=id OR 1=1导致全表误更新
需要按不同值更新多条记录?用 CASE WHEN + 参数绑定
当每条记录要设不同值(比如根据 ID 分别设置不同状态),不能循环执行 N 次 update,也不该拼 SQL。推荐用 update(...)->when() 或原生 CASE WHEN,并确保所有变量都绑定。
- ✅ 推荐写法(命名占位):
$sql = "UPDATE users SET status = CASE id<br> WHEN :id1 THEN :status1<br> WHEN :id2 THEN :status2<br> ELSE status END<br>WHERE id IN (:id1, :id2)";<br>Db::update($sql, [<br> 'id1' => 101, 'status1' => 2,<br> 'id2' => 102, 'status2' => 3<br>]);
- ⚠️ 注意:
Db::raw()不可用于包裹用户变量,仅限固定表达式(如Db::raw('NOW()'))
事务中批量更新仍需绑定,不可放松警惕
即使包裹在 Db::transaction() 中,也不能用字符串拼接——事务只保原子性,不提供注入防护,而批量更新常用于敏感操作(如库存扣减、余额调整)。
- ✅ 安全事务示例:
Db::transaction(function () use ($orders) {<br> $ids = array_column($orders, 'id');<br> Db::table('orders')->whereIn('id', $ids)->update(['status' => 'shipped']);<br>}); - ❌ 错误示范:
Db::transaction(function () use ($idsStr) {<br> Db::update("UPDATE orders SET status='shipped' WHERE id IN ({$idsStr})");<br>});


















