ci4 的 paginate() 总拿不到正确总数,因其与 countallresults() 共享 query builder 实例,链式条件(如 orlike、groupstart)在加 limit/offset 后易丢失;需拆为两次独立查询并重置条件。

CI4 的 paginate() 为什么总拿不到正确总数?
因为 paginate() 和 countAllResults() 共享同一个 Query Builder 实例,调用前者会自动加 LIMIT 和 OFFSET,再调后者时条件可能被重置或忽略——尤其带 orLike()、groupStart() 这类链式条件时,总数常变成全表记录数。
必须拆成两次独立查询:
- 先用完整条件查一次总数:
$total = $model->where(...)->orLike(...)->countAllResults(); - 再用同样条件查分页数据:
$items = $model->where(...)->orLike(...)->paginate(10);
- 别复用同一个模型实例做链式调用;每次查询前确保条件重置或新建实例
怎么让搜索分页的 base_url 带上 GET 参数?
CI4 默认只认 ?page=2,但你搜完再翻页,URL 得保留 ?q=xxx。直接拼 base_url 容易漏参数或重复编码。
推荐做法是用 current_url(true)(来自 url_helper)动态生成基础地址:
- 在控制器里先加载 helper:
helper('url'); - 配置分页时:
'base_url' => current_url(true) . '&page=' -
current_url(true)会返回当前完整 URL(含所有 GET 参数),&page=后续由 Pager 自动补数字 - 注意:不能写成
current_url(true) . '?page=',否则第二次翻页会变成?page=2?page=3
自定义分页 HTML 结构时,pager 模板参数怎么配才不打架?
默认输出是 <ul class="pagination"></ul>,但如果你用的是 Bootstrap 5 或 Tailwind,class 名和标签结构大概率冲突。
关键不是改 class,而是替换整个模板片段:
- 在视图中调用:
= $pager->links('default', 'custom') ?>,其中'custom'是模板名 - 在
app/Views/pager/下建custom.php,内容直接写 HTML,例如:<nav aria-label="Pagination"><ul class="flex space-x-1">= $pager->render() ?></ul></nav>
- 每个链接项用
$pager->getFirst() / getPrevious() / getNext() / getLast()手动控制,比靠 config 参数更可控 - 别依赖
first_tag_open这类旧参数——CI4 已弃用,设了也不生效
CI4 分页跳转后丢失 POST 数据或筛选状态怎么办?
分页链接本质是 GET 请求,POST 数据天然不传递。如果你的筛选表单是 POST 提交,翻页就会清空条件。
实际可行的解法只有两个:
- 把筛选表单改成
method="GET",所有字段变成 URL 参数,分页自然继承;缺点是 URL 变长,敏感字段暴露 - 用 session 临时存筛选条件:
session()->set('search_filter', $this->request->getPost());,然后在分页逻辑里读取:$model->where($session->get('search_filter')) - 千万别在分页链接里手动拼 POST 字段——容易 XSS,且无法处理数组型字段(如多选 checkbox)
最常被忽略的是:session 方案必须在每次请求开头就检查并清理过期条件,否则用户换关键词搜索后,旧 session 还在干扰分页总数计算。











