ThinkPHP怎么使用模型字段动态排序规则_ThinkPHP用户自选排序字段方式【教程】

阿强大大_4641

阿强大大_4641

2026-03-29

738人浏览

原创

必须使用白名单校验排序字段和方向,再以数组形式传入order(),禁止字符串拼接或db::raw()直传用户输入;分页需appends透传参数,缓存需包含排序字段。

thinkphp怎么使用模型字段动态排序规则_thinkphp用户自选排序字段方式【教程】

ThinkPHP 模型查询时怎么动态指定排序字段

不能直接把用户传来的字段名拼进 order(),否则会 SQL 注入。ThinkPHP 的 order() 方法本身不校验字段合法性,传入恶意字符串(比如 id, (SELECT password FROM think_user LIMIT 1))会直接执行。

正确做法是:先白名单过滤,再传入 order()。不是“支持任意字段”,而是“只支持你明确允许的字段”。

  • 定义一个可排序字段白名单数组,比如 ['id', 'create_time', 'sort', 'price']
  • 用 in_array() 判断用户请求的 sort_field 是否在白名单中
  • 同时检查排序方向(asc/desc),只接受这两个值,其他一律 fallback 到 asc
  • 构造 order() 参数时,用数组形式:[$field => $direction],避免字符串拼接

示例:

$allowFields = ['id', 'create_time', 'price', 'sort'];
$field = input('sort_field', 'id');
$dir = in_array(input('sort_dir', 'asc'), ['asc', 'desc']) ? input('sort_dir') : 'asc';

if (!in_array($field, $allowFields)) {
    $field = 'id';
}

$list = Db::name('goods')->order([$field => $dir])->select();

为什么不能用字符串拼接写 order('xxx '.input('dir'))

因为 order() 接收字符串时,ThinkPHP 不做任何字段名转义或关键字过滤,相当于裸 SQL 片段插入。一旦用户传入 sort_field=id; DROP TABLE think_user;(虽然实际执行受限于 MySQL 协议,但多语句或子查询仍可能触发),风险就不可控。

更隐蔽的问题是:某些字段名含下划线、大小写混合(如 user_name),或本身就是保留字(如 order、group),字符串拼接后没加反引号,会导致 SQL 报错或逻辑错乱。

  • ThinkPHP 6+ 的 order() 数组用法会自动给字段加反引号,字符串用法则不会
  • MySQL 严格模式下,未加反引号的保留字字段直接报错:ERROR 1064 (42000): You have an error in your SQL syntax
  • 即使字段名合法,方向参数若没过滤,传入 desc, sleep(5) 这类也能绕过基础校验

使用 Db::raw() 动态拼接排序字段的风险与替代方案

有人想用 Db::raw() 绕过白名单,比如 order(Db::raw(input('field').' '.input('dir'))) —— 这等于把闸门全打开,比字符串拼接更危险,因为 Db::raw() 明确告诉框架“别处理,原样上”。绝对不要这么做。

真正需要灵活字段(比如关联表字段、JSON 字段提取)时,应走另一条路:预定义规则 + 映射转换。

  • 用户传 sort_field=price_min,后端映射为 JSON_EXTRACT(price_range, "$.min")
  • 用户传 sort_field=author_name,后端关联 think_user 表并指定 user.nickname
  • 所有映射逻辑写死在代码里,不暴露原始字段名,也不接受任意表达式
  • 映射后的 SQL 片段仍需经 Db::raw(),但来源可控,非用户直输

例如:

$map = [
    'price_min' => Db::raw('JSON_EXTRACT(price_range, "$.min")'),
    'author_name' => 'user.nickname',
];
$field = $map[input('sort_field', 'id')] ?? $map['id'];
$list = Db::name('goods')->alias('g')
    ->join('think_user user', 'g.user_id = user.id')
    ->order($field . ' ' . ($dir === 'desc' ? 'DESC' : 'ASC'))
    ->select();

分页 + 动态排序时容易漏掉的坑

排序字段变化后,如果不把当前排序参数也带进分页链接,翻页时会回到默认排序(比如 id),用户会以为数据错乱。这不是 ThinkPHP 的 Bug,是开发者没显式透传参数。

  • 用 paginate() 时,通过 ->appends() 手动追加 sort_field 和 sort_dir
  • 注意 appends() 传的是键值对,不是 URL 字符串,别写成 appends('sort_field=price')
  • 如果用了路由变量(如 GET /list/:sort_field/:sort_dir),要确保分页生成的链接能正确识别这些变量
  • 缓存场景下,不同排序参数必须生成不同缓存 key,否则 price desc 的结果可能被 id asc 覆盖

示例:

$paginate = Db::name('goods')
    ->order([$field => $dir])
    ->paginate(['appends' => ['sort_field' => $field, 'sort_dir' => $dir]]);
字段名白名单和排序方向校验这两步,少一个都可能让整个排序功能变成注入入口。很多人测的时候只看“能排”,不看“能不能被利用”,上线后才出问题。

php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4283

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

911

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1109

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6201

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3043

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6180

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8361

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1130

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

992

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.7万人学习