orderRaw 是绕过字段校验、直插原生 SQL 排序表达式的出口,用于处理数据库函数、表达式等 order() 无法支持的复杂排序场景,但存在 SQL 注入等风险,需谨慎使用。
ThinkPHP 的 orderRaw 不是用来“增强排序”的,而是绕过字段校验、直插原生 SQL 排序表达式的出口。它不解析、不转义、不自动绑定参数,用得好能解决 order() 无法处理的复杂排序场景;用错则可能引发 SQL 注入、数据库兼容问题或静默失效。
当排序逻辑涉及数据库函数、表达式或非标准字段结构时,order() 会拒绝或忽略——因为它只认纯字段名 + ASC/DESC 组合。
CONVERT(name USING gbk) 或 ORDER BY CONVERT(name USING utf8mb4)
DATE_FORMAT(create_time, "%Y-%m-%d"),避免同一天内时间戳干扰顺序VARCHAR 存 "1", "2", "10",直接 order('field asc') 会排成 1, 10, 2;改用 orderRaw('CAST(field AS UNSIGNED) ASC') 才得 1, 2, 10orderRaw('status IS NULL, status ASC'),比 COALESCE(status, 999) 更清晰可控这是最典型也最容易翻车的场景。用户选中 [5, 2, 8, 1] 四条记录,要求结果严格按此顺序返回,不是升序也不是降序,而是业务定义的优先级。
FIELD(id, 5, 2, 8, 1) 最简:返回值为 1/2/3/4,升序即得目标顺序$query->orderRaw("FIELD(id, " . implode(',', array_map('intval', $ids)) . ")")
array_map('intval', $ids) 过滤,防止字符串 ID 导致 FIELD() 全返回 0(排在最后)in 和 FIELD() 内部 ID 列表必须完全一致,否则缺失 ID 不参与排序,结果数量对不上max_allowed_packet 错,建议拆分或改用临时表ThinkPHP 查询构建器会把所有 order() 和 orderRaw() 调用按调用顺序拼进最终 ORDER BY 子句。这意味着你可以分层控制排序逻辑。
立即学习“PHP免费学习笔记(深入)”;
orderRaw() 处理复杂优先级,再用 order() 补基础字段:$query->orderRaw('FIELD(status, 2, 1, 3)')->order(['create_time' => 'desc', 'id' => 'asc'])
ORDER BY FIELD(status, 2, 1, 3), create_time DESC, id ASC
orderRaw 中的字段若含别名(如 p.sort),必须确保该别名已在 join() 中正确定义,否则报 Column 'sort' in order clause is ambiguous
initialize() 或作用域里硬编码 $this->order(),它会覆盖外部链式调用,且无法清除orderRaw 是把双刃剑:自由度高,风险也高。它不校验、不拦截、不兜底。
?sort=id 或 ?dir=desc 不能直接塞进 orderRaw($input),必须白名单校验后走 order(['field' => 'asc'])
FIELD() 在 PostgreSQL/SQL Server 不可用,需换 CASE WHEN id = 5 THEN 1 ... ELSE 999999 END
ORDER BY 子句中绑定参数,orderRaw('FIELD(id, ?)', [$ids]) 会失败,只能手动拼接并确保数值安全'show_sql' => true,确认最终生成的 ORDER BY 是否符合预期,尤其注意反引号、空格、逗号位置