thinkphp分页必须用paginate()而非select()后切数组,因其执行数据库层分页(count+limit),避免全表加载导致oom;错误链式调用会退化为内存分页,总数不准、性能崩溃。

ThinkPHP分页为什么必须用 paginate() 而不是先 select() 再切数组
因为 paginate() 是数据库层分页,而先 select() 再分页是内存层分页——后者会把全表数据查出来再用 PHP 切片,一两万条记录就可能 OOM 或超时。
真实执行流程是:paginate() 自动发两条 SQL:一条 COUNT(*) 算总数,一条带 LIMIT offset, length 查当前页数据。offset = (currentPage() - 1) × list_rows。
- 错误写法:
Db::name('user')->select()->paginate(10)→select()已执行,返回的是 PHP 数组,paginate()只是对数组array_slice(),总数不准、性能崩塌 - 正确链式:
Db::name('user')->where('status', 1)->paginate(15)→ 框架控制整个查询生命周期 - 带 JOIN 时注意:COUNT 查询可能变慢,确保关联字段(如
user.id和profile.user_id)都有索引
list_rows 怎么影响总页数?它不是配置项而是计算因子
总页数不是你传进去的,是框架内部算出来的:lastPage() === ceil($count / $list_rows)。只要 $count 和 $list_rows 确定,结果唯一,改不了。
-
list_rows必须是正整数;传 0 或负数会触发保底逻辑(max(1, ceil())),导致总页数恒为 1 - 当
$count === 0(查不到数据),无论list_rows多大,lastPage()永远是 1 —— 这是 TP 的默认兜底行为,不是 bug - 在
paginate([15, ['list_rows' => 20]])中,第一个参数 15 是当前页码,第二个数组里的list_rows才生效;别把顺序搞反
URL 参数不透传,翻页就丢搜索条件和每页条数
框架默认只保留 page 参数,per_page=20、keyword=abc 这类参数点下一页时全没了——生成的链接变成 ?page=2,而不是 ?page=2&per_page=20&keyword=abc。
- 必须显式配置
query参数:->paginate(['list_rows' => input('per_page/d', 15), 'query' => ['per_page' => input('per_page/d', 15)]]) - 不要用
$_GET['per_page'],要用input('per_page/d', 15)做类型过滤,防注入导致list_rows变成 0 或字符串 - 前端切换条数时,建议用
location.href拼完整 URL 跳转,而不是仅提交page字段——否则容易丢失筛选条件
分页对象里哪些方法返回的是真实值,哪些只是“读取缓存”
$list->total()、$list->currentPage()、$list->lastPage() 都是只读访问器,它们不重新查库,只是返回内部已算好的值。改了 lastPage() 的返回值?没用,它根本不接受写入。
-
$list->total()来自 COUNT 查询结果,不可伪造;想让它变,只能改查询条件或数据源 -
$list->render()输出 HTML 分页栏,不是$list->show()(那是老版本 API) - 模板中数据循环直接用
{volist name="list" id="item"},$list既是分页对象,也实现了ArrayAccess,可直接遍历 - 如果接口走 POST 或 CLI,
paginate()默认从$_GET['page']读页码,必须手动传'page' => input('p/d', 1),否则永远是第一页
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











