在 ThinkPHP 開發的 CMS 系統中,長文章分頁是常見需求。本文詳細說明如何配合 UEditor 的分頁符 _ueditor_page_break_tag_ 實現內容分割、頁碼生成、URL 美化與靜態快取路由配置。
UEditor 預設分頁符為 _ueditor_page_break_tag_,可在 ueditor.config.js 自訂:
// ueditor.config.js
pageBreakTag: '_ueditor_page_break_tag_', // 預設值,建議保持
// 或自訂更短的標記
// pageBreakTag: '[pagebreak]',
<?php
namespace Home\Controller;
use Think\Controller;
class ArticleController extends Controller {
public function detail($id, $p = 1) {
$id = (int)$id;
$p = max(1, (int)$p);
$article = M('Article')->find($id);
if (!$article) $this->error('文章不存在');
// 1. 分割內容
$pages = explode('_ueditor_page_break_tag_', $article['content']);
$totalPages = count($pages);
// 2. 邊界檢查
if ($p > $totalPages) $p = $totalPages;
// 3. 當前頁內容(陣列索引從 0 開始)
$currentContent = $pages[$p - 1] ?? '';
// 4. 生成分頁 HTML
$pageHtml = $this->buildPageHtml($totalPages, $p, $id);
$this->assign([
'article' => $article,
'content' => $currentContent,
'pageHtml' => $pageHtml,
'currentPage' => $p,
'totalPages' => $totalPages,
]);
$this->display();
}
private function buildPageHtml(int $total, int $current, int $id): string {
if ($total <= 1) return '';
// 當前 URL 基礎(去除舊的 p 參數)
$url = U('Article/detail', ['id' => $id], '', true); // 獲取路由生成的 URL
$parse = parse_url($url);
$baseUrl = $parse['path'];
$query = [];
if (isset($parse['query'])) parse_str($parse['query'], $query);
unset($query['p']);
$queryStr = $query ? '?' . http_build_query($query) : '';
$html = '';
return $html;
}
}
<!-- Article/detail.html -->
<article class="article-detail">
<h1>{$article.title}</h1>
<div class="meta">
作者:{$article.author} | 發布:{$article.create_time|date='Y-m-d H:i'} | 分類:{$article.category_name}
</div>
<div class="content">
{$content} <!-- 當前頁內容 -->
</div>
<!-- 分頁導航 -->
{$pageHtml}
<!-- SEO:rel=prev/next -->
<?php if ($currentPage > 1): ?>
<link rel="prev" href="{:U('Article/detail', ['id'=>$article.id, 'p'=>$currentPage-1])}">
<?php endif; ?>
<?php if ($currentPage < $totalPages): ?>
<link rel="next" href="{:U('Article/detail', ['id'=>$article.id, 'p'=>$currentPage+1])}">
<?php endif; ?>
</article>
<style>
.article-pagination ul { display: flex; gap: 4px; list-style: none; padding: 10px 0; justify-content: center; }
.article-pagination a { padding: 6px 12px; border: 1px solid #ddd; border-radius: 3px; color: #333; text-decoration: none; }
.article-pagination a:hover { background: #f5f5f5; }
.article-pagination .active span { padding: 6px 12px; background: #337ab7; color: #fff; border-radius: 3px; }
.article-pagination .ellipsis { padding: 6px 8px; color: #999; }
</style>
ThinkPHP 靜態快取預設不支援動態參數,需在配置中顯式聲明 p 參數:
<?php
// Application/Common/Conf/config.php
return [
// ... 其他配置
'HTML_CACHE_ON' => true, // 開啟靜態快取
'HTML_CACHE_TIME' => 3600, // 快取秒數
'HTML_FILE_SUFFIX' => '.shtml', // 偽靜態後綴
'HTML_CACHE_RULES' => [
'Article:detail' => [
'{:module}/{:controller}/{:action}_{id}_{p}', // 關鍵:加入 {p} 參數
3600,
'p' => ['p' => 1] // 預設值
],
// 列表頁等其他規則...
],
];
生成的靜態檔案結構:
/Runtime/Html/Home/Article_detail_123_1.shtml // 第 1 頁
/Runtime/Html/Home/Article_detail_123_2.shtml // 第 2 頁
...
# nginx.conf
location / {
# 嘗試直接服務靜態快取檔案
try_files /Runtime/Html/$uri @thinkphp;
}
location @thinkphp {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
include fastcgi_params;
}
在模板 <head> 中動態輸出:
<?php if ($totalPages > 1): ?>
<?php if ($currentPage == 1): ?>
<link rel="canonical" href="{:U('Article/detail', ['id'=>$article.id], '', true)}">
<?php else: ?>
<link rel="canonical" href="{:U('Article/detail', ['id'=>$article.id, 'p'=>$currentPage], '', true)}">
<?php endif; ?>
<?php if ($currentPage > 1): ?>
<link rel="prev" href="{:U('Article/detail', ['id'=>$article.id, 'p'=>$currentPage-1], '', true)}">
<?php endif; ?>
<?php if ($currentPage < $totalPages): ?>
<link rel="next" href="{:U('Article/detail', ['id'=>$article.id, 'p'=>$currentPage+1], '', true)}">
<?php endif; ?>
<?php endif; ?>
| 問題 | 原因 | 解決 |
|---|---|---|
| 分頁連結點擊仍顯示第 1 頁內容 | 靜態快取規則缺少 {p} 參數 | 在 HTML_CACHE_RULES 中加入 _{p} 並設定預設值 |
| UEditor 內容儲存的分頁符不生效 | 編輯器配置與解析不一致 | 確認 pageBreakTag 與 explode 參數完全一致 |
| 靜態檔案未生成 | 目錄權限、快取開關、規則匹配 | 檢查 Runtime/Html 可寫、HTML_CACHE_ON、控制器名匹配 |
| 頁碼過多顯示擁擠 | 未做省略號處理 | 參考 buildPageHtml 只顯示當前頁附近 ±2 頁 |
掌握這套流程,即可在 ThinkPHP 專案中實現 SEO 友好、支援靜態快取的文章分頁功能。