php mvc分页需解耦查询、页码逻辑与渲染:url参数经请求对象安全提取并校验;模型提供getitems()和gettotalcount()方法;控制器组装分页上下文数组传给视图;视图用预构建链接安全渲染,处理空结果边界情况。

PHP分页在MVC中不是“套个模板就能跑”,关键在于把数据查询、页码逻辑、视图渲染三者解耦,同时避免在控制器里拼SQL或在模型里写HTML。
分页参数怎么从URL传到控制器
用户点击 /list?page=3&per_page=10 时,page 和 per_page 必须被安全提取。不要用 $_GET 直接取值,而是通过路由或请求对象统一处理:
-
page默认为 1,必须是正整数,否则强制设为 1 -
per_page应限制范围(如 5–50),防止恶意传per_page=999999导致全表扫描 - 注意 URL 中可能带其他参数(如搜索关键词),分页链接需保留它们,不能只写
?page=2
模型层怎么返回分页所需的数据和总数
模型不该返回完整数据集,而应提供两个方法:getItems() 和 getTotalCount()。两者共用同一套 where 条件,但后者只查 COUNT(*):
public function getItems($offset, $limit, $conditions = [])
{
$sql = "SELECT * FROM posts WHERE status = ? ORDER BY created_at DESC LIMIT ? OFFSET ?";
return $this->db->fetchAll($sql, [1, $limit, $offset]);
}
<p>public function getTotalCount($conditions = [])
{
$sql = "SELECT COUNT(*) FROM posts WHERE status = ?";
return (int) $this->db->fetchValue($sql, [1]);
}别在一个查询里用 LIMIT + SQL_CALC_FOUND_ROWS —— MySQL 8.0 已弃用,且在高并发下不准。
立即学习“PHP免费学习笔记(深入)”;
控制器怎么组装分页上下文并交给视图
控制器负责把原始参数转成视图能直接用的结构,比如:
- 当前页
$currentPage - 每页条数
$perPage - 总条数
$totalCount - 总页数
$totalPages = ceil($totalCount / $perPage) - 上一页/下一页链接(含所有保留参数)
别把生成页码 HTML 的逻辑放在控制器里。传给视图的应该是一个数组,例如:
$pagination = [
'current' => $currentPage,
'per_page' => $perPage,
'total' => $totalCount,
'pages' => range(1, $totalPages),
'prev_url' => $this->buildUrl(['page' => max(1, $currentPage - 1)]),
'next_url' => $this->buildUrl(['page' => min($totalPages, $currentPage + 1)]),
];视图里怎么安全渲染分页链接
视图只做两件事:循环显示数据、按 $pagination 渲染页码栏。重点是 URL 构建必须编码参数值,防止 XSS:
- 用
htmlspecialchars()处理所有输出到 HTML 的变量,包括$page和链接中的查询参数 - 不要手拼
<a href="?page=%E2%80%A6%EF%BC%8C%E8%80%8C%E6%98%AF%E7%94%A8%E9%A2%84%E6%9E%84%E5%BB%BA%E5%A5%BD%E7%9A%84%20%24pagination['prev_url']%20%E7%AD%89%E5%AD%97%E6%AE%B5 - %E9%A1%B5%E7%A0%81%E6%95%B0%E5%AD%97%E8%BF%87%E5%A4%9A%E6%97%B6%EF%BC%88%E6%AF%94%E5%A6%82%20100%20%E9%A1%B5%EF%BC%89%EF%BC%8C%E5%88%AB%E5%85%A8%E5%88%97%E5%87%BA%E6%9D%A5%EF%BC%8C%E8%A6%81%E5%8A%A0%E7%9C%81%E7%95%A5%E9%80%BB%E8%BE%91%EF%BC%88%E5%A6%82%201%20%E2%80%A6%205%206%207%208%209%20%E2%80%A6%20100%EF%BC%89%E2%80%94%E2%80%94%20%E8%BF%99%E9%83%A8%E5%88%86%E5%8F%AF%E6%8A%BD%E6%88%90%E4%B8%80%E4%B8%AA%E7%8B%AC%E7%AB%8B%E7%9A%84%20
PaginationRenderer%20%E7%B1%BB%EF%BC%8C%E4%BD%86%E4%B8%8D%E5%B1%9E%E4%BA%8E%E6%A8%A1%E5%9E%8B%E6%88%96%E6%8E%A7%E5%88%B6%E5%99%A8%E8%81%8C%E8%B4%A3
%E6%9C%80%E5%B8%B8%E8%A2%AB%E5%BF%BD%E7%95%A5%E7%9A%84%E6%98%AF%EF%BC%9A%E5%BD%93%20%24totalCount%20===%200%20%E6%97%B6%EF%BC%8C%24totalPages%20%E4%BC%9A%E6%98%AF%200%EF%BC%8Crange(1,%200)%20%E8%BF%94%E5%9B%9E%E7%A9%BA%E6%95%B0%E7%BB%84%EF%BC%8C%E9%A1%B5%E7%A0%81%E6%A0%8F%E7%9B%B4%E6%8E%A5%E6%B6%88%E5%A4%B1%E2%80%94%E2%80%94%E8%BF%99%E6%B2%A1%E9%97%AE%E9%A2%98%EF%BC%8C%E4%BD%86%E5%BE%97%E7%A1%AE%E4%BF%9D%E5%89%8D%E7%AB%AF%E4%B8%8D%E5%9B%A0%E6%AD%A4%E6%8A%A5%E9%94%99%E6%88%96%E7%95%99%E7%99%BD%E5%BC%82%E5%B8%B8%E3%80%82











