分頁是 Web 應用處理大量資料的核心技術。Yi 提供了從底層 CPagination 到高階 CActiveDataProvider、內建 Widget 的完整分頁體系。本文詳細對比三種主流實現方式。
public function actionIndex() {
$criteria = new CDbCriteria();
// 複雜查詢條件構建...
$count = Article::model()->count($criteria);
$pages = new CPagination($count);
$pages->pageSize = 10;
$pages->applyLimit($criteria); // 自動套用 LIMIT/OFFSET
$models = Article::model()->findAll($criteria);
$this->render('index', [
'models' => $models,
'pages' => $pages,
]);
}
<?php foreach ($models as $model): ?>
<div class="article">
<h3><?php echo CHtml::encode($model->title); ?></h3>
<p><?php echo CHtml::encode(mb_substr($model->content, 0, 200)); ?>...</p>
</div>
<?php endforeach; ?>
<!-- 分頁導航 -->
<?php $this->widget('CLinkPager', [
'pages' => $pages,
'header' => '',
'firstPageLabel' => '首頁',
'lastPageLabel' => '末頁',
'prevPageLabel' => '上一頁',
'nextPageLabel' => '下一頁',
'htmlOptions' => ['class' => 'pagination'],
]); ?>
public function actionIndex() {
$dataProvider = new CActiveDataProvider('News', [
'criteria' => [
'condition' => 'status = 1',
'order' => 'create_time DESC',
'with' => ['author'], // 預載入關聯
],
'pagination' => [
'pageSize' => 20,
'pageVar' => 'page', // URL 參數名
],
]);
$this->render('index', ['dataProvider' => $dataProvider]);
}
<?php $this->widget('zii.widgets.CListView', [
'dataProvider' => $dataProvider,
'itemView' => '_view', // 渲染單筆資料的部分視圖
'template' => "{items}\n{pager}",
'emptyText' => '暫無新聞資料',
'pager' => [
'class' => 'CLinkPager',
'header' => '',
'cssFile' => false,
],
]); ?>
<div class="news-item">
<h4><?php echo CHtml::link(CHtml::encode($data->title), ['view', 'id' => $data->id]); ?></h4>
<span class="meta">
作者:<?php echo $data->author->name; ?> |
時間:<?php echo date('Y-m-d', strtotime($data->create_time)); ?>
</span>
<p><?php echo CHtml::encode(mb_substr($data->content, 0, 150)); ?>...</p>
</div>
<?php $this->widget('zii.widgets.grid.CGridView', [
'id' => 'news-grid',
'dataProvider' => $model->search(),
'filter' => $model, // 啟用欄位篩選
'template' => "{items}\n{pager}",
'pager' => [
'class' => 'CLinkPager',
'header' => '分頁:',
'prevPageLabel' => '上一頁',
'nextPageLabel' => '下一頁',
'cssFile' => false,
],
'summaryText' => '顯示 {start}-{end} 筆,共 {count} 筆',
'columns' => [
'id',
[
'name' => 'title',
'htmlOptions' => ['width' => '30%'],
'value' => 'mb_substr($data->title, 0, 30, "utf-8")',
],
[
'name' => 'content',
'htmlOptions' => ['width' => '30%'],
'value' => 'mb_substr(strip_tags($data->content), 0, 50, "utf-8")',
],
[
'name' => 'type',
'value' => 'News::model()->getTypeLabel($data->type)',
'filter' => News::model()->getTypeOptions(), // 下拉篩選
],
'author.username', // 關聯欄位
[
'name' => 'status',
'value' => 'News::model()->getStatusBadge($data->status)',
'filter' => ['1' => '發布', '0' => '草稿', '-1' => '刪除'],
],
[
'class' => 'CButtonColumn',
'template' => '{view} {update} {delete}',
'buttons' => [
'delete' => [
'label' => '移除',
'options' => ['class' => 'btn-delete', 'data-confirm' => '確定刪除?'],
],
],
],
],
]); ?>
public function search() {
$criteria = new CDbCriteria;
$criteria->compare('id', $this->id);
$criteria->compare('title', $this->title, true);
$criteria->compare('content', $this->content, true);
$criteria->compare('type', $this->type);
$criteria->compare('user', $this->user, true);
$criteria->compare('status', $this->status);
$criteria->compare('create_data', $this->create_data, true);
return new CActiveDataProvider($this, [
'criteria' => $criteria,
'pagination' => ['pageSize' => 50],
'sort' => ['defaultOrder' => 'create_data DESC'],
]);
}
| 維度 | CPagination 手動 | CActiveDataProvider + CListView | CGridView |
|---|---|---|---|
| 適用場景 | 複雜自定 SQL、多表 JOIN、非 ActiveRecord 查詢 | 標準列表頁、RESTful API、前台展示 | 後台管理、需排序/篩選/批次操作的表格 |
| 程式碼量 | 最多 | 中等 | 最少(聲明式配置) |
| 彈性 | 最高 | 中等 | 受限於 GridView 架構 |
| 效能調校 | 完全控制 | 透過 criteria 優化 | 需注意關聯查詢 N+1 問題 |
| 前端整合 | 手工渲染 | 部分視圖複用 | 內建 AJAX 排序/分頁/篩選 |
// urlManager 規則
'rules' => [
'news/page/' => 'news/index',
'news//page/' => 'news/category',
]
// CListView / CGridView 啟用 AJAX
'ajaxUpdate' => true, // 預設開啟
'afterAjaxUpdate' => 'js:function(id, data) { console.log("分頁更新完成"); }',
// 避免 OFFSET 1000000 效能崩潰,改用游標分頁 $criteria->condition = 'id > :last_id'; $criteria->params[':last_id'] = $lastSeenId; $criteria->order = 'id ASC'; $criteria->limit = 20; // 前端改為「載入更多」按鈕而非頁碼導航
// 控制器
$cacheKey = "news_list_page_{$page}_size_{$pageSize}";
if (($data = Yii::app()->cache->get($cacheKey)) === false) {
$data = $dataProvider->getData();
Yii::app()->cache->set($cacheKey, $data, 300); // 快取 5 分鐘
}
根據業務場景選擇適合的分頁方式,結合快取、索引優化與前端體驗設計,構建高效能、用戶友好的列表展示系統。