laravel分页、排序、搜索必须协同工作:paginate()切数据、orderby()定顺序、where()筛条件;顺序错、参数漏、校验缺会导致丢参、超时、漏搜。

直接说结论:Laravel 的分页、排序、搜索不是三个独立功能,而是一套必须协同工作的链路——paginate() 负责切数据,orderBy() 决定顺序,where() 或 whereLike() 处理筛选;三者顺序错、参数漏、校验缺,页面就丢参数、接口就超时、搜索就漏结果。
paginate() 为什么总丢 order 和 search 参数
默认只保留 page,其他查询参数(如 order、search)在生成分页链接时被清空。这不是 bug,是 Laravel 故意设计的“安全默认”。
- 控制器里必须显式调用
appends(),且推荐用request()->only(['order', 'search', 'direction']),避免传入空值导致 URL 出现?search=&page=2 - 不要写
appends(['search' => request('search')])—— 当用户没输关键词时,request('search')返回null,会拼出无效参数 - 如果用了自定义分页视图(比如从
vendor发布出来的),检查模板里是否硬编码了appends(['page' => $page]),这会覆盖你传的所有业务参数
orderBy() + request() 组合容易踩的坑
orderBy(request('order', 'id'), request('direction', 'desc')) 看似简洁,但存在字段注入和排序失效风险。
-
request('order')可被前端篡改,比如传order=id; DROP TABLE users;(虽然 Laravel 默认不执行 SQL 注入,但字段名若未白名单校验,仍可能触发非法列名错误或慢查询) - 正确做法是先定义允许排序的字段白名单:
$allowedOrders = ['id', 'name', 'created_at'];,再用in_array(request('order'), $allowedOrders) ? request('order') : 'id' -
direction同样要限制为'asc'或'desc',否则传direction=xyz会导致 SQL 报错
搜索用 whereLike 还是 FULLTEXT?看数据量和字段结构
单字段模糊搜(如 title LIKE '%xxx%')适合小表(first_name + last_name)或需相关性排序时,MATCH ... AGAINST 是更稳的选择。
- 用
where('title', 'like', "%{$term}%")无法走索引,数据量一过 10 万,paginate()的COUNT(*)就开始卡顿 - 对
first_name和last_name建联合 FULLTEXT 索引后,可直接写:whereRaw("MATCH(first_name, last_name) AGAINST(? IN NATURAL LANGUAGE MODE)", [$term]) - 注意:MySQL 8.0+ 的 InnoDB 支持多列 FULLTEXT,但 MyISAM 已弃用;建索引迁移中必须用
$table->fullText(['first_name', 'last_name']),不能靠手动 SQL
大数据量下 paginate() 卡死?换 simplePaginate() 或游标分页
当表行数超 500 万,paginate(20) 会强制执行 SELECT COUNT(*),即使加了索引也慢——因为 InnoDB 的 COUNT 是逐行统计。
-
simplePaginate(20)不查总数,只取LIMIT 21判断“是否有下一页”,性能提升明显,但前端只能显示上/下页,不能跳转任意页 - 真正防卡死的是
cursorPaginate():它基于排序字段(如id)的值做游标,每次查WHERE id > ? LIMIT 20,彻底绕过 OFFSET 和 COUNT,但要求该字段有唯一索引且不可为空 - 别在没索引的字段上用
cursorPaginate(),否则可能漏数据或重复;也别在updated_at这类可能重复的字段上用
最常被忽略的一点:所有分页 + 排序 + 搜索组合,都得在控制器里统一做参数校验和截断——page 强制转整型,per_page 限制 1–100,search 截断长度(比如 max 100 字符),否则攻击者一个 per_page=10000 就能把数据库拖垮。











