Zend_Paginator

Zend_PaginatorZend Framework 1.6 的新功能之一

是用來提供作資料分頁的 Component,雖然分頁是個簡單的小功能,可是有時候自己寫又嫌麻煩,所以拿現成的元件來套用也蠻方便的。

Controller

要使用 Zend_Paginator 前,要知道 Zend_Paginator 支援下列四種方式:

  1. Array – 將資料集合以 Array 的形式傳入。
  2. DbSelect – 將資料庫查詢所需的 Select Query 以 Zend_Db_Select 或字串的形式傳入,會根據 Query 向 DB 抓取對應所需的資料。
  3. Iterator – 傳 Iteraotr ,沒仔細看,不過使用上應該跟 Array 差不多。
  4. Null – 不處理資料,只用來處理分頁控制的部份。

以 Array Adapter 為例,在 Controller 裡可以用

$paginator = new Zend_Paginator(new Zend_Paginator_Adapter_Array($array));

或是透過 factory pattern

$paginator = Zend_Paginator::factory($array);

接著設定頁數及每頁項目數量

$paginator->setCurrentPageNumber($page)
->setItemCountPerPage($numPerPage); // Default is 10
$this->view->paginator = $paginator;

View

而顯示的部份則交由 view script 來處理。這 View 的處理可分為兩個部份,一個是資料項目的顯示,另一個是分頁功能表的顯示。

資料項目的部份,由於 Zend_Paginator 實做 SPL 的 IteratorAggregate 介面,所以可以使用 foreach() 很方便地來詢訪資料項目。

分頁功能表(用來選擇上一頁、下一頁、或跳頁的控制像) 則透過 paginationControl() 這個 View Helper 來處理

以下為範例的程式碼

<html>
  <body>
    <h1>Example</h1>
    <?php if (count($this->paginator)): ?>
    <ul>
    <?php foreach ($this->paginator as $item): ?>
      <li><?= $item; ?></li>
    <?php endforeach; ?>
    </ul><?php endif; ?>
    <?= $this->paginationControl($this->paginator, 'Sliding', 'my_pagination_control.phtml'); ?>
  </body>
</html>

Zend_Paginator 預設提供四種 Scrolling (捲頁) Style: All、Elastic、Jumping和Sliding。Elastic 和 Sliding分別是 Google 和 Yahoo,不過除了 Elastic 會列出比較多頁數以外,我感覺好像沒什麼差別,下面這是 Sliding Style 的例子:

All 則是會把所有頁數全部列出來,可以用在使用 select box 選擇頁數的時候。

Manual 裡除了範例程式外,還提供了幾個常用的分頁控制項的的例子,其中有提到 YDN 的一些資源,是有關一些 UI 或 Web UI 常見的 pattern,還蠻不錯的,可以參考看看

Advance

再來討論一些我遇到的問題跟解決方法

第一個是 Zend_Db_Table 和 Zend_Paginator 的配合,目前雖然有 DbSelect Adapter 可以用,不過傳回來的東西會是 Array,但是因為我把一些邏輯寫在 Zend_Db_Table_Row 裡面,所以希望資料項目能是 Zend_Db_Table_Rowset 或 Zend_Db_Table_Row,所以有人寫了Paginator_Adapter_DbTable ,改寫 getItem() 的部份,透過 Zend_Db_Table 去作 fetchAll(),不過這個 Adapter 目前還沒有被官方正式採納,所以我就改成使用 Null Adapter 並且自己管理 Data Item。

$select = $table->select();
$select->where(...)->limitPage($page, $numPerPage);
$data = $table->fetchAll($select);
 
$selectCount = $table->select();
$selectCount->from(array("t"=>"table"), array("cnt"=>'COUNT(*)')->where(...);
$cnt = $table->fetchRow($selectCount);
$paginator = Zend_Paginator::factory($cnt);
$paginator->setCurrentPageNumber($page)
          ->setItemCountPerPage($numPerPage);

再來另外一個問題是有關 Zend_Db_Table 的 Relationship Operation,前面我自己寫兩次 Query 分別去取得資料跟總數已經是個爛方法了,可是如果我要用的資料是透過 findManyToManyRowset() 得到的,那就有問題了,似乎不能使用 from() 去設定 COUNT(*)。

Google 上查查看,找到這個作法,發現在呼叫完 findManyToManyRowset() 之後,$select 會被改寫成包含 (inner) join 的 SQL Query,所以我可以拿著這個 $select 去請 Zend_Paginator 幫我處理總資料數目。裡面提到會發生 Exception 的問題似乎已經解決了,所以直接用就可以了

$bugsTable = new Bugs();
$bugsRowset = $bugsTable->find(1234)->current();
 
$select = $bugsTable->select();
$select->where(...)->limitPage($page, $numPerPage);
$productsRowset = $bug1234->findManyToManyRowset('Products', 'BugsProducts', null, null, $select);
$paginator = Zend_Paginator::factory($select);
$paginator->setCurrentPageNumber($page)
          ->setItemCountPerPage($numPerPage);

另外原來自己查詢 COUNT(*)使用 Null Apdapter 的作法也可以同樣換掉

$select = $table->select();
$select->where(...)->limitPage($page, $numPerPage);
$data = $table->fetchAll($select);
$paginator = Zend_Paginator::factory($select);
$paginator->setCurrentPageNumber($page)
         ->setItemCountPerPage($numPerPage);

這樣的作法在 Controller 中看起來會滿簡潔的,不過我不確定在校能上會不會有問題,例如說是多做了一次 Query、或是每次都查詢所有的資料。

參考資料

相關文章:

Related posts brought to you by Yet Another Related Posts Plugin.

2 Responses to “Zend_Paginator”

  1. Temporality » Blog Archive » 一些 WordPress 的修改 Says:

    [...] Zend_Paginator [...]

  2. Temporality » Blog Archive » Zend Framework 1.7 Release Says:

    [...] support for Zend_Paginator, 應該是我之前想要的那個功能吧 [...]

Leave a Reply