Zend Paginator

The Zend Framework provides an extremely flexible component named Zend_Paginator to handle paginating your data to acceptable chunks. This class is capable of working with literally any collection.

The Zend_Paginator class provides a function named ‘factory’ that discovers the adapter type that it needs to invoke.. A few lines from the Paginator.php file are reproduced below:

public static function factory($data, $adapter = self::INTERNAL_ADAPTER,
                                   array $prefixPaths = null)
    {
        if ($adapter == self::INTERNAL_ADAPTER) {
            if (is_array($data)) {
                $adapter = 'Array';
            } else if ($data instanceof Zend_Db_Table_Select) {
                $adapter = 'DbTableSelect';
            } else if ($data instanceof Zend_Db_Select) {
                $adapter = 'DbSelect';
            } else if ($data instanceof Iterator) {
                $adapter = 'Iterator';
            } else if (is_integer($data)) {
                $adapter = 'Null';
            } else {
                $type = (is_object($data)) ? get_class($data) : gettype($data);
                /**
                 * @see Zend_Paginator_Exception
                 */
                require_once 'Zend/Paginator/Exception.php';
                throw new Zend_Paginator_Exception('No adapter for type ' . $type);
            }
        }

The basic syntax for using the zend_paginator component is

$paginator = Zend_Paginator::factory($collection); 
$paginator->setCurrentPageNumber($this->_getParam('page'));
$paginator->setItemCountPerPage(10);
$this->view->paginator =$paginator;

The most common use of the paginator is in conjunction with data tables. Proper use of this component requires that you pass in a ‘select’ object AND NOT THE COMPLETE DATASET. Please reread the previous sentence one more time if it did not sink in.. it really is the most critical aspect of using Zend_Paginator with database tables.

WRONG:

$users = new Users;
$data = $users->fetchAll();
$paginator = Zend_Paginator::factory($data);

Line 2 in the above code allocates memory for array $data and then initializes the paginator. So, if the Users table contains lots of rows, this will make your code extremely inefficient.

CORRECT:

$users = new Users;
$select = $users->select();
$paginator = Zend_Paginator::factory($select);

Here’s what my controller looks like:

<?php
class IndexController extends BaseController
{
   public function indexAction() 
    {
        $users = new Users();
        $select = $users->select();
        $paginator = Zend_Paginator::factory($select);
        $paginator->setCurrentPageNumber($this->_getParam('page'));
        $paginator->setItemCountPerPage(10);
        $this->view->paginator =$paginator;
    }
}

?>

The view then takes the paginator object that is passed to it and renders the data – index.phtml:

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  <title>ZendFramework Sample File</title>
<table border="1">
    <?php echo $this->partialLoop('partial-loops/user_display.phtml', $this->paginator) ?>
</table>
<?php echo $this->paginationControl($this->paginator, 'Sliding', 'pagination.phtml'); ?>
</head>
<body>
Welcome!
</body>
</html>

partialLoop is a view helper that is provided by the framework specifically for handling repetitive data. It takes the paginator object and properly formats the contents. The ‘views/scripts/partial-loops/user_display.phtml’ is really simple and looks like:

<tr>
<td><?php echo $this->username?></td>
<td><?php echo $this->user_type?></td>
</tr>

Note that $this in the context of the partialLoop is the collection/array itself.

The final piece of code is setting up the control that actually displays the page numbers with links at the bottom of the screen. This is done by the line

<?php echo $this->paginationControl($this->paginator, ‘Sliding’, ‘pagination.phtml’); ?>

The ‘views/scripts/pagination.phtml’ is as shown below:

<?php
//handle parameters during paging..
if ($this->pageCount)
{
    $params = Zend_Controller_Front::getInstance()->getRequest()->getParams();
    //remove the system params
    unset($params['module']);
    unset($params['controller']);
    unset($params['action']);
}
?>
<div id="pagination-digg">
<ul>
    <!-- Previous page link -->
    <?php if (isset($this->previous)): ?>
    <li class="previous"><a href="&lt;?php echo $this-&gt;url(array_merge($params,array('page' =&gt; $this-&gt;previous))); ?&gt;">< Previous</a></li>
    <?php else: ?>
        <li class="previous-off">< Previous</li>
    <?php endif; ?>
    <!-- Numbered page links -->
    <?php foreach ($this->pagesInRange as $page): ?>
        <?php if ($page != $this->current): ?>
    <li ><a href="&lt;?php echo $this-&gt;url(array_merge($params,array('page' =&gt; $page))); ?&gt;"><?php echo $page; ?></a></li>
        <?php else: ?>
            <?php echo "<li class='active'>".$page."</li>"; ?>
        <?php endif; ?>
    <?php endforeach; ?>
    <!-- Next page link -->
    <?php if (isset($this->next)): ?>
    <li class="next"><a href="&lt;?php echo $this-&gt;url(array_merge($params,array('page' =&gt; $this-&gt;next))); ?&gt;">Next ></a></li>
    <?php else: ?>
    <li class="next-off">Next ></li>
    <?php endif; ?>
</ul>
</div>

Notice the use of the $params variable to gather parameters being passed to the page. This approach allows you to share your pagination code throughout your application (as against passing the 4th ‘options’ parameter while invoking paginationControl). The parameters are then appended to the url using the array_merge() function. Simple enough..

Styling id’s are inserted at appropriate locations while generating the HTML.

Finally, the style sheet used to format the page links to look like those used on digg.com is listed below (pagination-digg.css):

#pagination-digg li {
        border: 0;
        margin-right:2px;
        padding: 0;
        font-size: 11px;
        list-style: none; 
        float: left;
}

#pagination-digg a {
        border:solid 1px #9aafe5;
        margin-right: 2px;
}

#pagination-digg .previous-off,#pagination-digg .next-off {
        border:solid 1px #DEDEDE;
        color:#888888;
        display:block;
        float:left;
        font-weight:bold;
        margin-right:2px;
        padding:3px 4px;
}

#pagination-digg .next a,#pagination-digg previous a {
        
        font-weight: bold;
}

#pagination-digg .active {
        color:#FFFFFF;
        background:#2e6ab1;
        font-weight: bold;
        display: block;
        float: left;
        padding: 4px 6px; 
}

#pagination-digg a:link,#pagination-digg a:visited {
        color: #0e509e;
        display: block;
        float: left;
        padding: 3px 6px;
        text-decoration: none;
}

#pagination-digg a:hover {
        text-decoration: none;
        border:solid 1px #0e509e
}

That’s pretty much all there is to it!

Advertisements

5 thoughts on “Zend Paginator

  1. Nice example, and it works just fine,
    but i have one question.
    How can i echo an ordinal number in my partialLoop file?

    1 or 2 or 3
    username?>
    user_type?>

    1. Hi,
      The trick is to save the state in the parent view using the ‘PartialLoop’ view helper using:

      $this->getHelper(‘PartialLoop’)->view

      example:
      index.phtml

      names =array(‘first’=>array(‘Id’=>’1’,
      ‘LastName’=>’last’,
      ‘FirstName’=>’first’),
      ‘second’=>array(‘Id’=>’2’,
      ‘LastName’=>’last1’,
      ‘FirstName’=>’first1’)
      );
      $this->counter=1;
      ?>
      partialLoop(‘partialList.phtml’, $this->names); ?>

      Id Last First

      partialList.phtml

      getHelper(‘PartialLoop’)->view->counter++)?> escape($this->FirstName); ?> escape($this->LastName); ?>
  2. i am have used your code and it worked fine…but now i want to use it with JOIN QUERY..

    Can any1 plz explain how Zend_Paginator can be made to work with JOIN STATEMENT

    1. In order to have the paginator fetch only the required rows, it must essentially must be passed a Zend_Db_Select object. This can be accomplished as follows:

      $db = Zend_Db_Table::getDefaultAdapter();
      //assuming default adapter is set in the config file
      $select = $db->select()->from(‘A’)->join(‘B’, $joinCondition);
      $paginator = Zend_Paginator::factory($select);

      (Remember that the Table Data Gateway (inheriting from the Zend_Db_Table_Abstract) pattern is necessarily an object-oriented interface to a single table at a time)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s