ThinkPHP权重排序需在数据库层实现,核心是设计top_weight字段、用orderRaw显式排序、避免布尔字段和模型order覆盖;动态打分应下推SQL计算,前端排序须白名单校验。

ThinkPHP 实现权重排序,核心是把“谁该排前面”这个业务逻辑,准确、高效地落到数据库查询里,而不是靠 PHP 循环计算。重点不在写几个 order(),而在于设计字段、构造表达式、控制边界——否则容易卡顿、错序、难维护。
加一个靠谱的权重字段
别用 is_top 这种布尔字段应付置顶需求。它只能分两组,组内无序,运营一填就乱套。直接在文章表加 top_weight INT UNSIGNED DEFAULT 0 字段:
- 普通文章保持 0,不干扰原有时间排序
- 置顶文章填正整数(如 100、99、98),数值越大越靠前
- 数据库层面禁掉负数,从根上防输错
- 模型验证加 rule:['number', 'between:0,999'],限制合理范围
排序语句必须用 orderRaw 显式拼接
TP6 的 order() 是覆盖式,不是叠加式。如果模型作用域、基类控制器、中间件里都写了 order,最后那个生效,极易被覆盖。安全写法是统一收口:
- 在模型里定义 scope 方法:
public function scopeWithTopSort($query) { $query->orderRaw('top_weight DESC, create_time DESC'); } - 调用时只写
ArticleModel::scopeWithTopSort()->select() - 开启 show_sql,确认最终 SQL 真的是
ORDER BY top_weight DESC, create_time DESC
需要动态打分?让数据库实时算,别存字段
比如按点击量 + 时间衰减综合排序,千万别建一个 weight 字段定时更新——数据一过期就失真,大表 UPDATE 还锁库。正确做法是把计算逻辑下推到 SQL:
立即学习“PHP免费学习笔记(深入)”;
- TP6 推荐:
->field('*, (click * 0.7 + POW(UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(create_time), 0.3)) AS score'),再->orderRaw('score DESC') - 注意函数名大小写(UNIX_TIMESTAMP 不是 unix_timestamp),别拼错
- 点击累加用
Db::execute("UPDATE article SET click = click + 1 WHERE id = ?"),绕过模型层,快且轻量
前端传参排序要白名单校验
用户点表头切换 create_time desc,不能直接把 request 参数塞进 order(),否则字段名注入风险极高:
- 硬编码允许字段:
$allowed = ['create_time', 'score', 'view_count']; - 检查传入字段是否在列表中,type 是否为 asc/desc,其他一律忽略
- 关联字段(如 user.nickname)需显式 join,不能靠点号自动解析



















