# 機能設計書 62-ページネーション

## 概要

本ドキュメントは、LEGACY CMSにおけるページネーション機能の設計を記述する。本機能はZend_Paginatorコンポーネントを使用して、一覧表示時のページ分割処理を実現する。

### 本機能の処理概要

本機能は、記事・イベント・ユーザー・コメントなど、大量のデータを一覧表示する際に、1ページあたりの表示件数を制限し、ページ送りナビゲーションを提供する。データベースクエリの結果をZend_Paginatorでラップすることで、効率的なページ分割を実現する。

**業務上の目的・背景**：大量のコンテンツを持つCMSにおいて、全件を一度に表示するとページの読み込み時間が長くなり、ユーザビリティが低下する。また、データベースへの負荷も増大する。本機能により、適切な件数でデータを分割表示し、快適なユーザー体験と効率的なシステムリソース利用を実現する。

**機能の利用シーン**：
- 記事一覧画面での記事リスト表示
- イベント一覧画面でのイベントリスト表示
- ユーザー管理画面でのユーザーリスト表示
- コメント管理画面でのコメントリスト表示
- 検索結果画面での検索ヒットコンテンツ表示
- アセット管理画面でのファイルリスト表示

**主要な処理内容**：
1. データベースクエリ（Zend_Db_Select）を生成
2. Zend_Paginator::factory()でPaginatorオブジェクトを生成
3. 現在のページ番号、1ページあたりの件数、ページ範囲を設定
4. ビューにPaginatorオブジェクトを渡す
5. ビューでデータをループ表示し、ページナビゲーションを描画

**関連システム・外部連携**：Zend_Paginatorコンポーネントと連携してページ分割処理を実現する。

**権限による制御**：ページネーション自体には権限制御はないが、表示対象のコンテンツ一覧へのアクセスは各機能の権限チェックに依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ホーム画面 | 参照画面 | 記事一覧のページ送り処理 |
| 3 | 検索結果画面 | 主画面 | 検索結果のページ送り処理 |
| 9 | 記事一覧画面 | 主画面 | 記事一覧のページ送り処理 |
| 10 | カテゴリ別記事一覧画面 | 主画面 | 記事一覧のページ送り処理 |
| 11 | 著者別記事一覧画面 | 主画面 | 記事一覧のページ送り処理 |
| 12 | アーカイブ画面 | 主画面 | 記事一覧のページ送り処理 |
| 14 | タグ別記事一覧画面 | 主画面 | 記事一覧のページ送り処理 |
| 16 | イベント一覧画面 | 主画面 | イベント一覧のページ送り処理 |
| 19 | カテゴリ別イベント一覧画面 | 主画面 | イベント一覧のページ送り処理 |
| 20 | 会場別イベント一覧画面 | 主画面 | イベント一覧のページ送り処理 |
| 25 | タグ別ページ一覧画面 | 主画面 | ページ一覧のページ送り処理 |
| 35 | 記事管理画面 | 主画面 | 記事一覧のページ送り処理 |
| 41 | ユーザー管理画面 | 主画面 | ユーザー一覧のページ送り処理 |
| 48 | イベント管理画面 | 主画面 | イベント一覧のページ送り処理 |
| 60 | ページ管理画面 | 主画面 | ページ一覧のページ送り処理 |
| 64 | メール管理画面 | 主画面 | メール一覧のページ送り処理 |
| 75 | アセット管理画面 | 主画面 | アセット一覧のページ送り処理 |
| 85 | コメント管理画面 | 主画面 | コメント一覧のページ送り処理 |
| 87 | ローテーター管理画面 | 主画面 | ローテーター一覧のページ送り処理 |

## 機能種別

データ取得 / ページ分割処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| page | int | No | 表示するページ番号（デフォルト: 1） | 数値、正の整数 |
| items | int | No | 1ページあたりの表示件数（デフォルト: 15） | 数値、正の整数 |
| range | int | No | ページナビゲーションに表示するページ数（デフォルト: 5） | 数値、正の整数 |

### 入力データソース

- URLクエリパラメータまたはルーティングパラメータから取得
- コントローラーで設定されるデフォルト値

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| paginator | Zend_Paginator | ページネーション情報を含むイテレータオブジェクト |
| paginator->current | int | 現在のページ番号 |
| paginator->last | int | 最終ページ番号 |
| paginator->previous | int/null | 前のページ番号（なければnull） |
| paginator->next | int/null | 次のページ番号（なければnull） |
| paginator->pagesInRange | array | 表示するページ番号の配列 |
| paginator->pageCount | int | 総ページ数 |

### 出力先

- ビューテンプレートでのデータループ表示
- ページナビゲーションコントロールの描画

## 処理フロー

### 処理シーケンス

```
1. リクエストパラメータの取得
   └─ page, items, rangeパラメータを取得、未指定時はデフォルト値を使用

2. データベースクエリの生成
   └─ Zend_Db_Select オブジェクトを生成（フィルタ条件、ソート順を含む）

3. Paginatorオブジェクトの生成
   └─ Zend_Paginator::factory($select) でPaginatorを生成

4. ページネーション設定の適用
   └─ setCurrentPageNumber(), setItemCountPerPage(), setPageRange() を呼び出し

5. ビューへのPaginator渡し
   └─ $this->view->xxxArray = $paginator として設定

6. ビューでのデータ表示
   └─ foreach($this->xxxArray as $item) でループ表示

7. ページナビゲーションの描画
   └─ $this->paginationControl() でナビゲーションを出力
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[パラメータ取得]
    B --> C[DBクエリ生成]
    C --> D[Paginator生成]
    D --> E[ページ番号設定]
    E --> F[件数/範囲設定]
    F --> G[ビューへ渡す]
    G --> H[データループ表示]
    H --> I{総ページ数 > 1?}
    I -->|Yes| J[ページナビ表示]
    I -->|No| K[ナビ非表示]
    J --> L[完了]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-62-01 | デフォルト件数 | 1ページあたりのデフォルト表示件数は15件 | itemsパラメータ未指定時 |
| BR-62-02 | デフォルトページ範囲 | ナビゲーションに表示するページ数のデフォルトは5 | rangeパラメータ未指定時 |
| BR-62-03 | 初期ページ | pageパラメータ未指定時は1ページ目を表示 | pageパラメータ未指定時 |
| BR-62-04 | ページナビ表示条件 | 総ページ数が1より大きい場合のみナビゲーションを表示 | 常時 |
| BR-62-05 | スライディングスクロールスタイル | ページナビゲーションはSlidingスタイルを使用 | 常時 |

### 計算ロジック

ページネーションの計算はZend_Paginatorが内部で行う:

1. 総件数: SQLのCOUNTクエリで取得
2. 総ページ数: ceil(総件数 / 1ページあたり件数)
3. 現在ページのオフセット: (現在ページ番号 - 1) * 1ページあたり件数
4. LIMIT/OFFSETをSQLに追加して該当ページのデータを取得

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 記事一覧 | articles, articles_categories, users | SELECT | 記事データをJOINで取得 |
| イベント一覧 | events, events_categories, events_venues | SELECT | イベントデータをJOINで取得 |
| ユーザー一覧 | users, users_profiles, users_roles | SELECT | ユーザーデータをJOINで取得 |
| コメント一覧 | comments, users | SELECT | コメントデータをJOINで取得 |
| アセット一覧 | assets, users | SELECT | アセットデータを取得 |

### テーブル別操作詳細

Zend_Paginatorは内部で以下の2つのクエリを発行する:

#### カウントクエリ
| 操作 | 項目（カラム名） | 取得条件 | 備考 |
|-----|-----------------|---------|------|
| SELECT | COUNT(*) | 元のSELECTと同じWHERE条件 | 総件数取得用 |

#### データ取得クエリ
| 操作 | 項目（カラム名） | 取得条件 | 備考 |
|-----|-----------------|---------|------|
| SELECT | 指定カラム | WHERE条件 + LIMIT offset, count | 現在ページのデータ取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無効なページ番号 | pageに0以下や非数値が指定された場合 | Zend_Paginatorが自動的に1ページ目を表示 |
| - | 範囲外のページ番号 | 存在しないページ番号が指定された場合 | 最終ページまたは空のリストを表示 |

### リトライ仕様

本機能ではリトライは不要。不正なパラメータは自動的にデフォルト値に補正される。

## トランザクション仕様

ページネーションはSELECTのみのため、トランザクション制御は不要。

## パフォーマンス要件

- Zend_PaginatorはLIMIT/OFFSETを使用するため、大規模データでも効率的にデータを取得
- カウントクエリとデータ取得クエリで2回のDB接続が発生
- 適切なインデックスが設定されていれば、ページ送りのレスポンスは高速

## セキュリティ考慮事項

- pageパラメータは数値チェックを行い、SQLインジェクションを防止
- フィルタ条件は適切にエスケープされる（Zend_Db_Selectのプレースホルダ機能）

## 備考

- 本機能はZend_Paginatorコンポーネントを標準的な方法で使用
- ビューヘルパー paginationControl() でナビゲーションを描画
- 管理画面とフロントエンドで異なるテンプレート（default.phtml）を使用
- Slidingスクロールスタイルは、現在ページを中心にページ番号を表示

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: モデルでのPaginator生成を理解する

まず、モデルクラスでPaginatorがどのように生成されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Articles.php | `application/models/Articles.php` | fetchArticles()でのPaginator生成パターン |

**読解のコツ**: Zend_Db_Selectでクエリを構築し、Zend_Paginator::factory()に渡すパターンを理解する。

**主要処理フロー**:
1. **101-162行目**: fetchArticles() メソッド
   - **109-136行目**: Zend_Db_Selectでクエリを構築（フィルタ・ソート）
   - **138-154行目**: パラメータのデフォルト値設定
   - **156-159行目**: Paginator生成と設定
   ```php
   $paginator = Zend_Paginator::factory($select);
   $paginator->setCurrentPageNumber($pagenum);
   $paginator->setItemCountPerPage($items);
   $paginator->setPageRange($range);
   ```

#### Step 2: コントローラーでのパラメータ設定を理解する

コントローラーでパラメータがどのように設定されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ArticlesController.php | `application/modules/admin/controllers/ArticlesController.php` | manageAction()でのパラメータ設定 |

**主要処理フロー**:
- **34-35行目**: デフォルト値をリクエストにセット
  ```php
  $this->_request->setParam('items',15);
  $this->_request->setParam('range',5);
  ```
- **42行目**: モデルを呼び出しPaginatorを取得
  ```php
  $this->view->articlesArray = $articles->fetchArticles($params);
  ```

#### Step 3: ビューでのデータ表示を理解する

ビューテンプレートでデータがどのように表示されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | manage.phtml | `application/modules/admin/views/scripts/articles/manage.phtml` | Paginatorのイテレート表示 |

**主要処理フロー**:
- **122-148行目**: Paginatorをforeachでループ
  ```php
  <?php if (count($this->articlesArray)) : ?>
  <?php foreach($this->articlesArray as $article) : ?>
  ```
- **151-158行目**: ページナビゲーションの表示
  ```php
  <?php if (count($this->articlesArray)) : ?>
  <div class="aNav">
      <?php echo $this->paginationControl($this->articlesArray,'Sliding','_pagination/default.phtml'); ?>
  ```

#### Step 4: ページナビゲーションテンプレートを理解する

ページナビゲーションの描画テンプレートを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | default.phtml (admin) | `application/modules/admin/views/scripts/_pagination/default.phtml` | 管理画面用ページナビテンプレート |
| 4-2 | default.phtml (default) | `application/modules/default/views/scripts/_pagination/default.phtml` | フロントエンド用ページナビテンプレート |

**主要処理フロー**:
- **8-10行目**: ページ数チェックとページ情報表示
  ```php
  if ($this->pageCount): ?>
  <div class="nButWrapL">Page <?php echo $this->current; ?> of <?php echo $this->last; ?></div>
  ```
- **15-19行目**: 前ページリンク
- **22-28行目**: ページ番号リンク（Slidingスタイル）
- **31-35行目**: 次ページリンク

#### Step 5: 別のモデルでの実装を確認

他のモデルでも同じパターンが使われていることを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | UsersController.php | `application/modules/admin/controllers/UsersController.php` | manageAction()でのPaginator使用 |

**主要処理フロー**:
- **180-187行目**: コントローラー内でPaginator生成
  ```php
  $paginator = Zend_Paginator::factory($select);
  $paginator->setCurrentPageNumber($this->view->page);
  $paginator->setItemCountPerPage(15);
  $paginator->setPageRange(5);
  $this->view->usersArray = $paginator;
  ```

### プログラム呼び出し階層図

```
Controller::xxxAction()
    │
    ├─ $this->_request->setParam('items', 15)
    ├─ $this->_request->setParam('range', 5)
    │
    ├─ Model::fetchXxx($params)
    │      ├─ $registry->db->select()  [クエリ構築]
    │      │      ├─ ->from()
    │      │      ├─ ->join()
    │      │      ├─ ->where()
    │      │      └─ ->order()
    │      │
    │      └─ Zend_Paginator::factory($select)
    │             ├─ ->setCurrentPageNumber($page)
    │             ├─ ->setItemCountPerPage($items)
    │             └─ ->setPageRange($range)
    │
    └─ $this->view->xxxArray = $paginator

View Template
    │
    ├─ foreach($this->xxxArray as $item)  [データ表示]
    │
    └─ $this->paginationControl($this->xxxArray, 'Sliding', '_pagination/default.phtml')
           │
           └─ _pagination/default.phtml
                  ├─ $this->current
                  ├─ $this->last
                  ├─ $this->previous
                  ├─ $this->next
                  └─ $this->pagesInRange
```

### データフロー図

```
[入力]                    [処理]                              [出力]

URLパラメータ ──────┐
  ・page           │
  ・items          │
  ・range          │
                   │
デフォルト値 ──────┼──▶ Controller ──▶ Model ──▶ Zend_Paginator
                   │                              │
DBクエリ結果 ──────┘                              │
                                                  ▼
                                          [Paginatorオブジェクト]
                                                  │
                                                  ▼
                                             View Template
                                                  │
                                    ┌─────────────┴─────────────┐
                                    ▼                           ▼
                              [データ一覧]              [ページナビゲーション]
                               foreach()               paginationControl()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Articles.php | `application/models/Articles.php` | ソース | 記事モデル、fetchArticles()でPaginator生成 |
| Comments.php | `application/models/Comments.php` | ソース | コメントモデル、Paginator生成 |
| Pages.php | `application/models/Pages.php` | ソース | ページモデル、Paginator生成 |
| ArticlesController.php (admin) | `application/modules/admin/controllers/ArticlesController.php` | ソース | 記事管理コントローラー |
| UsersController.php | `application/modules/admin/controllers/UsersController.php` | ソース | ユーザー管理コントローラー、Paginator直接生成例 |
| CommentsController.php | `application/modules/admin/controllers/CommentsController.php` | ソース | コメント管理コントローラー |
| ArticlesController.php (default) | `application/modules/default/controllers/ArticlesController.php` | ソース | 記事表示コントローラー |
| EventsController.php (default) | `application/modules/default/controllers/EventsController.php` | ソース | イベント表示コントローラー |
| manage.phtml | `application/modules/admin/views/scripts/articles/manage.phtml` | テンプレート | 記事一覧画面 |
| default.phtml (admin) | `application/modules/admin/views/scripts/_pagination/default.phtml` | テンプレート | 管理画面用ページナビテンプレート |
| default.phtml (default) | `application/modules/default/views/scripts/_pagination/default.phtml` | テンプレート | フロントエンド用ページナビテンプレート |
| search.phtml | `application/modules/default/views/scripts/_pagination/search.phtml` | テンプレート | 検索結果用ページナビテンプレート |
| comments.phtml | `application/modules/default/views/scripts/_pagination/comments.phtml` | テンプレート | コメント用ページナビテンプレート |
