thinkphp分页需在paginate前一次性链式写完所有条件、关联和字段,否则预加载失效或统计不准;join分页应手动指定去重count;传参须用数组并设list_rows;软删除需确保全局作用域生效。

ThinkPHP 模型 where 条件分页怎么写才不丢数据
直接用 where + paginate 是最常见写法,但容易漏掉关联字段或预加载失效。核心问题不在分页本身,而在查询构建时机——paginate 内部会重新 clone 查询对象,若你在 where 后又调用了 with 或 field,这些操作大概率被忽略。
- 正确姿势:所有条件、关联、字段筛选必须在调用
paginate前一次性链式写完,例如Article::where('status', 1)->with('author')->field('id,title,author_id')->paginate(10) - 别在
paginate后再调with,那只是给返回的集合补关联,不影响 SQL 查询,分页总数也不对 - 如果条件是动态拼装的,建议用查询对象变量承接:
$query = Article::where('deleted', 0); $query = $condition ? $query->where('type', $condition) : $query; return $query->paginate(15);
带 JOIN 的分页为什么 count SQL 报错或结果不准
ThinkPHP 默认分页会自动生成 COUNT(*) 子查询,但遇到 JOIN(尤其是 LEFT JOIN)时,它不会自动去重,导致统计行数远大于实际分页条目数。更糟的是,某些数据库(如 MySQL 8.0+)对子查询中含 JOIN 的 COUNT 限制更严,直接抛出 Invalid use of group function 类错误。
- 手动指定 count 查询:用
paginate(['query' => $params, 'total' => $count]),其中$count单独查一次去重后的数量,比如Article::alias('a')->join('user u', 'a.author_id = u.id')->count('DISTINCT a.id') - 避免在分页主查询里写
GROUP BY,ThinkPHP 的 count 自动推导会崩;真要分组聚合,改用selectPage手动分页 - MySQL 下若用
SQL_CALC_FOUND_ROWS,记得关掉found_rows配置('use_sql_calc_found_rows' => false),否则和 JOIN 冲突
paginate 方法传参传数组还是数字?参数名容易搞混
paginate 看似简单,但传参方式决定行为差异极大:传数字只设每页条数;传数组才能控制总数、页面、查询参数等。很多人卡在翻页链接带不出自定义参数,其实是没把 query 放进数组里。
- 基础用法:
paginate(10)—— 每页 10 条,当前页由 GET 参数page决定 - 带自定义参数:
paginate(['list_rows' => 10, 'page' => $p, 'query' => ['keyword' => $kw, 'type' => $t]]),这样生成的分页链接才会保留keyword和type -
page参数不是必须,留空就自动读$_GET['page'];但如果你用 API 方式调用(无 GET),就必须显式传'page' => $page_no - 别写
paginate(['size' => 10]),ThinkPHP 不认size,只认list_rows
软删除模型分页时 deleted_at 字段怎么过滤才安全
用 SoftDelete trait 的模型,默认 select 和 paginate 都会自动加 WHERE deleted_at IS NULL,但这个行为依赖全局作用域是否启用。一旦你手动调了 withoutTrashed() 或 onlyTrashed(),后续 paginate 就不会再自动过滤,极易漏掉逻辑删的数据或把已删数据查出来。
- 确认模型开启了软删除:检查是否声明
use SoftDelete;且有protected $deleteTime = 'deleted_at'; - 查正常数据,什么也不用加,
Article::paginate(10)就够;想查已删除的,必须显式写Article::onlyTrashed()->paginate(10) - 如果用了
scope自定义查询,记得在 scope 里也调$query->useSoftDelete(),否则 paginate 会绕过软删除逻辑 - 调试时可打印 SQL:
echo Article::getLastSql();,看生成的 count 和 select 语句里有没有deleted_at IS NULL
分页不是套个方法就完事,真正麻烦的是 count 和 select 两条 SQL 的一致性。只要中间插了任何非链式、非原子的操作,就可能让总数和列表对不上——这点在联表、软删除、动态字段场景下尤其隐蔽。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











