在Lara vel 11里,直接写原生SQL其实没那么可怕,前提是用对方法。很多人一上来就试图用DB::raw()执行整条语句——但这家伙根本不执行,它只是个标记。真正干活的是这四个:DB::select()、DB::insert()、DB::update()、DB::delete()。记住一点:DB::raw()只配当零件,别把它当发动机。

SELECT 查询必须用 DB::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() 只能嵌入构建器,不是执行入口
很多人误以为 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() 时,如果没消费完结果集,后续查询还可能失败——细节见真章。
