一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

在Laravel 11中如何安全地编写原始SQL查询语句?

时间:2026-07-02 11:17:02 编辑:袖梨 来源:一聚教程网

DB::select()专用于SELECT查询,返回stdClass对象数组并自动预处理防注入;其他操作应使用对应专用方法,禁用DB::raw()执行SQL,注意占位符、类型安全与事务显式管理。

直接用 DB::select()DB::insert()DB::update()DB::delete() 四个方法,别碰 DB::raw() 执行语句——它根本不执行,只是个标记。

SELECT 查询必须用 DB::select()

这个方法专为 SELECT 设计,返回 stdClass 对象数组,自动启用 PDO 预处理,天然防注入。传错类型(比如塞 INSERT)不会报错,但结果为空,MySQL 非严格模式下尤其难发现。

  • 只支持 ? 占位符或命名绑定(如 :name),但命名绑定需显式传 PDO 选项:DB::select('SELECT * FROM users WHERE name = :name', ['name' => 'Alice'], [PDO::ATTR_EMULATE_PREPARES => true])
  • IN 子句不能写成 WHERE id IN (?)——PDO 不支持数组绑定,得手动拼占位符:DB::select("SELECT * FROM users WHERE id IN (" . implode(',', array_fill(0, count($ids), '?')) . ")", $ids)
  • 日期建议先用 Carbon::parse($date)->toDateTimeString() 格式化,避免时区差异导致漏查
  • 返回值全是字符串(TINYINT(1) 字段返回 "1" 而非布尔值),不走模型生命周期,$casts、访问器全失效

写操作别用 DB::statement() 替代专用方法

DB::insert()DB::update()DB::delete() 返回明确语义(影响行数或布尔值),强制参数绑定,比 DB::statement() 更安全可控。

  • DB::insert() 不返回自增 ID;要取 ID 得改用 DB::table()->insertGetId()DB::getPdo()->lastInsertId()
  • DB::update()DB::delete()WHERE 条件务必先验证——漏条件或类型不匹配可能误改/删全表
  • 批量插入别循环调 DB::insert();应拼成单条 INSERT INTO ... VALUES (),(),() 再用 DB::statement()
  • DB::statement() 底层是 PDO::exec(),不返回数据集,写 SELECT 1 会直接报错

DB::raw() 只能嵌入构建器,不是执行入口

它本质是告诉查询构建器“这段字符串我信,别转义”,本身不执行任何 SQL。常见误用:把整条 SQL 包进 DB::raw() 然后试图运行——只会生成一个无意义的 Raw 实例,毫无效果。

  • 正确用法是在 select()whereRaw()orderByRaw() 中嵌入片段,例如:User::select('id', DB::raw('DATE_FORMAT(created_at, "%Y-%m") as month'))->get()
  • whereRaw() 仍需手动传参数数组:->whereRaw('created_at > DATE_SUB(NOW(), INTERVAL ? DAY)', [7])
  • 别在 whereRaw() 里拼用户输入:->whereRaw("name = '{$name}'") 是严重注入漏洞
  • 字段别名必须显式写出:DB::raw('COUNT(*) as total'),否则结果里找不到 total

最易被忽略的是:所有原生查询都不自动参与 Eloquent 的延迟加载、批量优化或模型事件;事务需显式用 DB::transaction() 包裹,混用 DB::select()DB::statement() 还可能因未消费结果集导致后续查询失败。

热门栏目