ThinkPHP权重排序实现与配置步骤
作者:技术拾光时间:2026-07-15 19:32:19浏览:521
ThinkPHP权重排序核心在于字段设计、排序语句构造与边界控制。使用top_weight整数字段控制置顶顺序,通过orderRaw显式拼接排序,避免覆盖。动态打分下推至SQL实时计算,前端传参需白名单校验,防止注入。
ThinkPHP 权重排序,最核心的思路其实就一句话:把“谁该排前面”的业务逻辑,落到数据库查询里,而不是靠 PHP 循环一通算。具体来说,无非是设计好字段、用对排序方法、管好前端传入的边界——下面展开聊聊几个关键点。

实现权重排序,重点不在写几个 order(),而在于字段设计、表达式构造、边界控制——否则很容易遇到卡顿、错序、维护困难的问题。下面直接上干货。
权重字段设计:使用 INT UNSIGNED 替代布尔字段
千万别用 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
这样做的好处是:所有排序逻辑集中在一个地方,后续维护一目了然,也不会被其他地方的 order 意外覆盖。
动态打分:让数据库实时计算,避免字段存储
比如按点击量 + 时间衰减综合排序,千万别建一个 weight 字段定时更新——数据一过期就失真,大表 UPDATE 还会锁库。正确做法是把计算逻辑下推到 SQL:
- 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,不能靠点号自动解析
别小看这一步,很多线上问题都是因为前端传了不存在的字段名或者恶意构造的字段导致 SQL 报错甚至数据泄漏。白名单校验是安全底线。
