OxyScripts.com
Menu spacer Home Tutorials Articles Code Forums irc.freenode.net #oxyscripts
Main (PHP)
Home Forums PHP News PHP Tutorials Articles PHP Code Snippets Contact Us Sysadmin Resources Books Template Shop
3rd Party Streams
SlashDot PHPDeveloper.org PHP.Net
Resources
PHP Manual MySQL Manual Smarty Manual PEAR Manual PHP-GTK Manual Symfony Manual
Code Snippets
Authentication Database Graphics HTTP Miscellaneous Time/Date
Affiliates
Scripts TutorialMan TutorialGuide CodingForums.com PHP Scripts Cheap Web Hosting Affordable Web Hosting Dreamweaver Templates

Search This Site :     PHP Function Reference :
 

How to paginate a list

Overview

Symfony provides a pager component: the sfPropelPager object. It can separate a list of results from a criteria object (of Criteria class) into a set of pages for display, and offers access methods to pages and result objects.

The sfPropelPager object

The sfPropelPager class uses the Propel abstraction layer, as described in the Model chapter.

This chapter will illustrate the way to use the sfPropelPager methods with a simple example : displaying a list of articles ten by ten. Assume that the Article object has ->getPublished(), ->getTitle(), ->getOverview() and ->getContent() accessor methods.

If you wanted to have the non-paginated result of a criteria request showing only published articles, you would need:

class articleActions extends sfActions
{
  public function executeList()
  {
    ...
    $c = new Criteria();
    $c->add(ArticlePeer::PUBLISHED, true);
    $articles = ArticlePeer::doSelect($c);
    $this->articles = $articles;
    ...
  }
}

The $articles variable, accessible from the template, would contain an array of all the Article objects matching the request.

To get a paginated list, you need a slightly different approach; the results have to be put in a sfPropelPager object instead of an array:

class articleActions extends sfActions
{
  public function executeList()
  {
    ...
    $c = new Criteria();
    $c->add(ArticlePeer::PUBLISHED, true);
    $pager = new sfPropelPager('Article', 10);
    $pager->setCriteria($c);
    $pager->setPage($this->getRequestParameter('page', 1));
    $pager->init();
    $this->pager = $pager;
    ...
  }
}

The differences start after the criteria definition, since this action:

  • creates a new pager to paginate Article objects ten by ten
  • affects the criteria to the pager
  • sets the current page to the requested page or to the first one
  • initializes the pager (i.e. execute the request related to the criteria)
  • passes the pager to the template via the $pager variable

The template listSuccess.php can now access the sfPropelPager object. This object knows the current page and the list of all the pages. It has methods to access pages and objects in pages. Let's see how to manipulate it.

To display the total number of results, use the ->getNbResults() method:

<?php echo $pager->getNbResults() ?> results found.<br />
Displaying results <?php echo $pager->getFirstIndice() ?> to  <?php echo $pager->getLastIndice() ?>.

To display the articles of the requested page, use the ->getResults() method of the pager object to retrieve the objects in the page:

<?php foreach ($pager->getResults() as $article): ?>
  <?php echo link_to($article->getTitle(), 'article/read?id='.$article->getId()) ?>
  <?php echo $article->getOverview() ?>
<?php endforeach ?>

Navigating across pages

The pager object knows if the number of results exceeds the maximum number that can be displayed in one page (10 in this example) thanks to the ->haveToPaginate() method.

To add the page navigation links at the bottom of the list (« < > »), use the navigation methods ->getFirstPage(), ->getPreviousPage(), ->getNextPage() and ->getLastPage(). The current page is given by ->getPage(). All these methods return an integer : the rank of the requested page.

To point to a specific page, loop through the collection of links obtained by a call to the ->getLinks() method:

<?php if ($pager->haveToPaginate()): ?>
  <?php echo link_to('&laquo;', 'article/list?page='.$pager->getFirstPage()) ?>
  <?php echo link_to('&lt;', 'article/list?page='.$pager->getPreviousPage()) ?>
  <?php $links = $pager->getLinks(); foreach ($links as $page): ?>
    <?php echo ($page == $pager->getPage()) ? $page : link_to($page, 'article/list?page='.$page) ?>
    <?php if ($page != $pager->getCurrentMaxLink()): ?> - <?php endif ?>
  <?php endforeach ?>
  <?php echo link_to('&gt;', 'article/list?page='.$pager->getNextPage()) ?>
  <?php echo link_to('&raquo;', 'article/list?page='.$pager->getLastPage()) ?>
<?php endif ?>

This should render something like:

   « < 1 - 2 - 3 - 4 - 5 > »

Once the article displayed, to allow a direct navigation to the previous or the next article without going back to the paginated list, you will need a cursor.

Navigating across objects

Navigating page by page within the list is easy, but the users might not want to go back to the list to navigate object by object. The cursor attribute of the sfPropelPager object can hold the offset of the current object.

This will allow an article by article navigation in the readSuccess.php template. First, let's modify a bit of the code of the listSuccess.php template:

<?php $cursor = $pager->getFirstIndice(); foreach ($pager->getResults() as $article): ?>
  <?php echo link_to($article->getTitle(), 'article/read?cursor='.$cursor) ?>
  <?php echo $article->getOverview() ?>
<?php ++$cursor; endforeach ?>

The read action will need to know how to handle a cursor parameter:

class articleActions extends sfActions
{
  public function executeRead()
  {
    ...
    if ($this->getRequestParameter('cursor'))
    {
      $article = $pager->getObjectByCursor($this->getRequestParameter('cursor'));
    }
    else if ($this->getRequestParameter('id'))
    {
      $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
    }
 
    // Error
    $this->forward404Unless($article);
  }
}

The ->getObjectByCursor($cursor) method sets the cursor at a specified position and returns the object at that very position.

You can set the cursor without getting the resulting object with the ->setCursor($cursor) method. And once the cursor is set, you can grab the current object at this position (->getCurrent()) but also the previous one (->getPrevious()) and the next one (->getNext()).

This means that the read action can pass to the template the necessary information for an article-by-article navigation with a few modifications:

class articleActions extends sfActions
{
  public function executeRead()
  {
    ...
    if ($this->getRequestParameter('cursor'))
    {
      $pager->setCursor($this->getRequestParameter('cursor'));
      $previous_article = $pager->getPrevious();
      $article = $pager->getCurrent();
      $next_article = $pager->getNext();
    }
    else if ($this->getRequestParameter('id'))
    {
      $article = ArticlePeer::retrieveByPK($this->getRequestParameter('id'));
    }
 
    // Error
    $this->forward404Unless($article);
  }
}

Note:The ->getPrevious() and ->getNext() methods return null if there is no previous or next object.

The readSuccess.php template could look like:

<h1><?php echo $article->getTitle() ?></h1>
<p class="overview"><?php echo $article->getOverview() ?></p>
<div class="content">
  <?php echo $article->getContent() ?>
</div>
&lt; <?php echo link_to_if($previous_article, $previous_article->getTitle(), 'article/read?id='.$previous_article->getId()) ?>
-
&gt; <?php echo link_to_if($next_article, $next_article->getTitle(), 'article/read?id='.$next_article->getId()) ?>

Changing the sort order

As the sfPropelPager object relies on a Criteria object, changing the order of the pager is simply done by adding a sort to the criteria, before assigning it to the pager object.

For instance, you can add the choice of the sort column to the list navigation interface:

class articleActions extends sfActions
{
  public function executeList()
  {
    ...
    $c = new Criteria();
    $c->add(ArticlePeer::PUBLISHED, true);
    if ($this->getRequestParameter('sort'))
    {
      $c->addAscendingOrderByColumn(ArticlePeer::translateFieldName($this->getRequestParameter('sort'), BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_COLNAME));
    }
    else
    {
      // sorted by date by default
      $c->addAscendingOrderByColumn(ArticlePeer::UPDATED_AT);
    }
    $pager = new sfPropelPager('Article', 10);
    $pager->setCriteria($c);
    $pager->init();
    $this->pager = $pager;
    ...
  }
}

Add the following to the listSuccess.php template:

Sort by : <?php echo link_to('Title', 'article/list?sort=title') ?> - <?php echo link_to('Id', 'article/list?sort=Id' ?>

Changing the number of results per page

The ->setMaxPerPage($max) method changes the number of results displayed per page, without the need to reprocess the pager (no need to call init() again). If you pass the value 0 for parameter, the pager will display all the results in one single page.

class articleActions extends sfActions
{
  public function executeList()
  {
    ...
    $c = new Criteria();
    $c->add(ArticlePeer::PUBLISHED, true);
    $pager = new sfPropelPager('Article', 10);
    $pager->setCriteria($c);
    if ($this->getRequestParameter('maxperpage'))
    {
      $pager->setMaxPerPage($this->getRequestParameter('maxperpage'));
    }
    $pager->init();
    $this->pager = $pager;
    ...
  }
}

So you can add the following to the listSuccess.php template:

Display : <?php echo link_to('10', 'article/list?maxperpage=10' ?> - <?php echo link_to('20', 'article/list?maxperpage=20' ?> results per page

Changing the select method

If you need to optimize the performance of an action relying on a sfPropelPager, you might want to force the pager to use a doSelectJoinXXX() instead of a simple doSelect(). This is easily achieved by the ->setPeerMethod() method of the sfPropelPager object:

$pager->setPeerMethod('doSelectJoinUser');

Note that the pager actually processes the doSelect() query when displaying a page. The first query (triggered by $pager->init()) does only a doCount, and you can also customize this method by calling:

$pager->setPeerCountMethod('doCountJoinUser');

Storing additional information in the pager

You may need sometimes to keep a certain context in a pager object. That's why the sfPropelPager class can handle parameters in the usual way:

$pager->setParameter('foo', 'bar');
 
if ($pager->hasParameter('foo'))
{
  $pager->getParameter('foo');
  $pager->getParameterHolder()->removeParameter('foo');
}
 
$pager->getParameterHolder()->clearParameters();

These parameters are never used directly by the pager.

To learn more about custom parameters, refer to the parameter holder chapter.

 
   Print this page

Top Sponsor
Symantec\'s Norton SystemWorks 2006
Sponsors
CA
Sponsors
AdWords Dominator 125*125
Advertisting

Affiliates
VertexTemplates PHPFreaks CodeWalkers StarGeek DevScripts CGI & PHP Scripts PHP CMS

Shopping Rebates   Sell It 4 You   Flash Page Counters   Get Insured
GPS Tracking Service   Charity Donate Info   Web Site Hosting   VOIP Service

Privacy Policy | Links | Site Map | Advertising

All content on OxyScripts.com is (©)2002-2007

 
Powered by Adrastea - Version 1.0.0. Copyright © Rune Solutions, 2004-2005