Code snippets for symfony 1.x

Navigation

Pager for array of objects

sfPropelPager allow you to use sufferless pagination but it only works for Objects. Sometimes, you can't get you array of Object using a Criteria. Here is a class that you can use exactly as the sfPropelPager but witch works using an array of objects, not a Criteria or peer classes or even a custom SQL (see timu EREN's snipeet). It has only a small differences with the original sfPropelPager.

You use it exactly the same way as you would with the sfPropelPager object.

Here is a small example:

In your action.

      $Ps = $category->getProducts();
      // here you can use any kind of array of objects
      $pager = new myTabPager($Ps, 15);
      $pager->setPage($this->getRequestParameter('page', 1));
      $pager->init();
      $this->pager = $pager;

The myTabPager class

<?php
 
/*
 * This file is an addon to the symfony package
 */
 
/**
 * @package    symfony
 * @subpackage addon
 * @author     Fabien Potentier modified by Karl
 */
 
/**
 *
 * myTabPager class.
 *
 * @package    symfony
 * @subpackage addon
 * @author     Fabien Potentier modified by Karl
 */
class myTabPager
{
  private
    $page                   = 1,
    $tab                    = array(),
    $maxPerPage             = 0,
    $lastPage               = 1,
    $nbResults              = 0,
    $objects                = null,
    $cursor                 = 1,
    $parameters             = array(),
    $currentMaxLink         = 1,
    $parameter_holder       = null;
 
  public function __construct($tab, $defaultMaxPerPage = 10)
  {
    $this->setTab($tab);
    $this->setMaxPerPage($defaultMaxPerPage);
    $this->setPage(1);
    $this->parameter_holder = new sfParameterHolder();
  }
 
  public function init()
  {
    $this->setNbResults(count($this->tab));
 
    if (($this->getPage() == 0 || $this->getMaxPerPage() == 0))
    {
      $this->setLastPage(0);
    }
    else
    {
      $this->setLastPage(ceil($this->getNbResults() / $this->getMaxPerPage()));
    }
  }
 
  public function setTab($tab)
  {
      $this->tab = $tab;
  }
 
  public function getTab()
  {
      return $this->tab;
  }
 
  public function getCurrentMaxLink()
  {
    return $this->currentMaxLink;
  }
 
  public function getLinks($nb_links = 5)
  {
    $links = array();
    $tmp   = $this->page - floor($nb_links / 2);
    $check = $this->lastPage - $nb_links + 1;
    $limit = ($check > 0) ? $check : 1;
    $begin = ($tmp > 0) ? (($tmp > $limit) ? $limit : $tmp) : 1;
 
    $i = $begin;
    while (($i < $begin + $nb_links) && ($i <= $this->lastPage))
    {
      $links[] = $i++;
    }
 
    $this->currentMaxLink = $links[count($links) - 1];
 
    return $links;
  }    
 
  public function haveToPaginate()
  {
    return (($this->getPage() != 0) && ($this->getNbResults() > $this->getMaxPerPage()));
  }
 
  public function getCursor()
  {
    return $this->cursor;
  }
 
  public function setCursor($pos)
  {
    if ($pos < 1)
    {
      $this->cursor = 1;
    }
    else if ($pos > $this->nbResults)
    {
      $this->cursor = $this->nbResults;
    }
    else
    {
      $this->cursor = $pos;
    }
  }
 
  public function getObjectByCursor($pos)
  {
    $this->setCursor($pos);
 
    return $this->getCurrent();
  }
 
  public function getCurrent()
  {
    return $this->retrieveObject($this->cursor);
  }
 
  public function getNext()
  {
    if (($this->cursor + 1) > $this->nbResults)
    {
      return null;
    }
    else
    {
      return $this->retrieveObject($this->cursor + 1);
    }
  }
 
  public function getPrevious()
  {
    if (($this->cursor - 1) < 1)
    {
      return null;
    }
    else
    {
      return $this->retrieveObject($this->cursor - 1);
    }
  }
 
  private function retrieveObject($offset)
  {
    return $this->tab[$offset];
  }
 
  public function getResults()
  {
    return array_slice($this->tab,($this->getPage() - 1) * $this->getMaxPerPage(),$this->maxPerPage);
  }
 
  public function getFirstIndice()
  {
    if ($this->page == 0)
    {
      return 1;
    }
    else
    {
      return ($this->page - 1) * $this->maxPerPage + 1;
    }
  }
 
  public function getLastIndice()
  {
    if ($this->page == 0)
    {
      return $this->nbResults;
    }
    else
    {
      if (($this->page * $this->maxPerPage) >= $this->nbResults)
      {
        return $this->nbResults;
      }
      else
      {
        return ($this->page * $this->maxPerPage);
      }
    }
  }
 
  public function getNbResults()
  {
    return $this->nbResults;
  }
 
  private function setNbResults($nb)
  {
    $this->nbResults = $nb;
  }
 
  public function getFirstPage()
  {
    return 1;
  }
 
  public function getLastPage()
  {
    return $this->lastPage;
  }
 
  private function setLastPage($page)
  {
    $this->lastPage = $page;
    if ($this->getPage() > $page)
    {
      $this->setPage($page);
    }
  }
 
  public function getPage()
  {
    return $this->page;
  }
 
  public function getNextPage()
  {
    return min($this->getPage() + 1, $this->getLastPage());
  }
 
  public function getPreviousPage()
  {
    return max($this->getPage() - 1, $this->getFirstPage());
  }
 
  public function setPage($page)
  {
    $page = intval($page);
 
    $this->page = ($page <= 0) ? 1 : $page;
  }
 
  public function getMaxPerPage()
  {
    return $this->maxPerPage;
  }
 
  public function setMaxPerPage($max)
  {
    if ($max > 0)
    {
      $this->maxPerPage = $max;
      if ($this->page == 0)
      {
        $this->page = 1;
      }
    }
    else if ($max == 0)
    {
      $this->maxPerPage = 0;
      $this->page = 0;
    }
    else
    {
      $this->maxPerPage = 1;
      if ($this->page == 0)
      {
        $this->page = 1;
      }
    }
  }
 
  public function getParameterHolder()
  {
    return $this->parameter_holder;
  }
 
  public function getParameter($name, $default = null, $ns = null)
  {
    return $this->parameter_holder->get($name, $default, $ns);
  }
 
  public function hasParameter($name, $ns = null)
  {
    return $this->parameter_holder->has($name, $ns);
  }
 
  public function setParameter($name, $value, $ns = null)
  {
    return $this->parameter_holder->set($name, $value, $ns);
  }
}
 
?>
by Charles tutu on 2006-08-25, tagged array  pager  pagination 

Comments on this snippet

gravatar icon
#1 donharold on 2006-08-28 at 07:38

I think this is only useful in some special cases, and can become a problem if you don't know what you are doing.

While you say it can be used almost exactly like sfPropelPager, there is a huge difference of another kind: Your way of doing it requires to load all the data from your datasource first. This will multiply the load for your database connection (or other datasource) and the memory usage for the server compared to paged loading of data. For low traffic sites with small data tables this might not be an issue, but it will significantly degrade performance for most sites, and in extreme cases (large tables and several concurrent users) can even make your server run out of memory.

I can see a use for your code when your data source only allows for the complete loading of its data anyway - for example, when displaying records from a parsed Excel or CSV file. But in the typical situations where you would use a pager (data from db or webservices), this is bad practice at best.

Since your introduction text and usage example kind of insinuate using it with a database, I think this should be mentioned.

gravatar icon
#2 Olivier Verdier on 2006-08-28 at 07:52

Good point, donharold!

Another comment is that this class is almost a copy/paste of the sfPropelPager class, only much less powerful. In those cases it is much wiser to subclass and overwrite only the necessary methods. Such a subclass of sfPropelPager would be no more than 20 lines long for the same result.

gravatar icon
#3 Olivier Verdier on 2006-11-25 at 09:09

This snippet should be removed because the class above should inherit from the sfPager class.

gravatar icon
#4 Charles tutu on 2006-11-29 at 10:51

That's right, I should have extended the sfPropelPager class. However, all vars in sfPropelPager are 'private' so they can't be used in the extended class (same as several classes...) As a mother class can't access her childs private vars (and child can't access his mother's private vars ). Setting the sfPropelPager vars and functions to be 'proteted' instead of 'private' would have let me extend this class. (I might be wrong however, but I think it is the way private methods and vars work)

Anyway, remember to use this class only when the sfPropelPager can't suit. That was my case.

gravatar icon
#5 Olivier Verdier on 2006-11-29 at 11:01

I was referring to the sfPager class, not the sfPropelPager class. Besides, both classes now have all their variables protected.

I understand your point but it is never a good idea to copy/paste an entire class. You should definitely amend or remove this snippet when symfony 1.0 is out.

gravatar icon
#6 scott meves on 2007-05-08 at 11:31

here is an updated version: http://www.symfony-project.com/snippets/snippet/177