Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "pagination"

nonHydratingPager class (a pager for when you don't want to hydrate your Doctrine object)

This is just a version of the nonHydratingPager for Propel, adapted for Doctrine.

<?php
/* Designed to be compatible with sfDoctrinePager only accept raw sql queries instead
 * of a Doctrine_Query object and spit back a resultset which you most likely want to
 * fill an array with instead of the standard array of hydrated objects.
 *
 * This solution is based on Propel's by Noel Tarnoff, Oz Basarir, dev AT (NOSPAM) naturalcapitalDOTorg
 *
 * @author  David Morales, davidmoralesmojica AT (NOSPAM) gmailDOTcom
 *
 * Typical usage scenario:
 * 1) build 2 query strings with common WHERE clause, one for count one for selecting the rows
 * 2) pass the queries in with page and maxPerPage into the constructor ( no need to ->init() )
 * 3) iterate through your result set (array of values)
 * 4) sit back and watch the fun
 *
 * ex.
 *  $results = new nonHydratingPager($query_select, $query_count, $page, $max);
 *
 *  foreach( $results as $result )
 *  {
 *    ...
 *  }
 */
 
 
class nonHydratingPager extends sfPager
{
  private $resultSet = null;
  private $query = null;
  private $query_count = null;
 
  public function __construct($query, $query_count, $page = 1, $maxPerPage = 25)
  {
    $this->setPage($page);
    $this->setMaxPerPage($maxPerPage);
    $this->query = $query;
    $this->query_count = $query_count;
  }
 
  public function init()
  {
    $rs = Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($this->query_count);
 
    $this->setNbResults($rs[0]['count']);
 
    $this->setLastPage(ceil($this->getNbResults() / $this->getMaxPerPage()));
 
    $startIndex = (($this->getPage()) - 1) * $this->getMaxPerPage();
 
 
    $this->query .= ' LIMIT ' . $this->getMaxPerPage() . ' OFFSET ' . $startIndex;
 
    $this->resultSet = Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($this->query);
  }
 
  public function getResults() {
    return $this->resultSet;
  }
 
  protected function retrieveObject($offset) {}
}
 
by David Morales on 2009-04-02, tagged doctrine  nohydrate  nonhydrating  nonhydratingpager  pager  pagination  rawsql  sql 

nonHydratingPager class (a pager for when you dont want to hydrate your propel object)

<?php
/* Designed to be compatible with sfPropelPager only accept raw sql queries instead
 * of a criteria object and spit back a resultset which you most likely want to
 * fill an array with instead of the standard array of hydrated objects.
 *
 * @author  Noel Tarnoff, Oz Basarir dev AT (NOSPAM) naturalcapitalDOTorg
 *
 * Typical usage scenario:
 * 1) build 2 query strings with common WHERE clause, one for count one for selecting the rows
 * 2) pass the queries in with page and maxPerPage into the constructor ( no need to ->init() )
 * 3) iterate through your result set and build your array of values
 * 4) sit back and watch the fun
 *
 * ex.
 *  $objPager = new nonHydratingPager($query_select, $query_count, $page, $max);
 *
 *  $rs = $objPager->getResultSet();
 *
 *  $arrEntities = array();
 *  while ( $rs->next() ) {
 *     $arrEntities[$] = array('field1'=>$rs->getString(1),
 *                             'field2'=>$rs->getString(2),
 *                             'field3'=>$rs->getString(3));
 *  }
 */
 
 
class nonHydratingPager extends sfPager
{
private
  $resultSet = null;
 
  public function __construct($query, $query_count, $page = 1, $maxPerPage = 25)
  {
    $this->setPage($page);
    $this->setMaxPerPage($maxPerPage);
 
    $con = Propel::getConnection();
 
    $stmt = $con->prepareStatement($query_count);
    $rs = $stmt->executeQuery(ResultSet::FETCHMODE_NUM);
    $rs->next();
 
    $this->setNbResults($rs->get(1));
 
    $this->setLastPage(ceil($this->getNbResults() / $this->getMaxPerPage()));
 
    $startIndex = (($this->getPage()) - 1) * $maxPerPage;
 
 
    $query .= ' LIMIT ' . $maxPerPage . ' OFFSET ' . $startIndex;
    $stmt = $con->prepareStatement($query);
 
    $this->resultSet = $stmt->executeQuery(ResultSet::FETCHMODE_NUM);
  }
 
  public function init() {}
 
  public function getResults() {
    return $this->resultSet;
  }
 
  protected function retrieveObject($offset) {}
 
}
by noel on 2007-02-21, tagged nohydrate  nonhydrating  nonhydratingpager  pager  pagination  propel 
(4 comments)

Complimentary Filters

A common modification to a CRUD module is filtering records on the list page. There are probably several nice ways to do this, including drop-downs, forms, etc. depending on the situation. While some of these approaches are nice, they are often not minimal... We like minimal!

Discussion

Say you have a book table, with a related author and genre. You want to filter the list of books by those related tables with links, paginate your list, and make it sortable via links in the header of the table.

To do this traditionally, you'll need lots of nasty little if statements in your template code, to check for all of these parameters (author_id, genre_id, title, num_pages, page, etc.) and combine them all together into a meaningful uri for your link_to function (something like "book/list?author_id=10&genre_id=5&sort=title&page=5"). We still want that link in the end, but generating it... How about a better way!

Followup Example

<!-- listSuccess.php -->
<?php use_helpers('Filter', 'Pagination') ?>
<h2>Books</h2>
<h3>Filter By</h3>
<table class="filters">
  <tbody>
    <tr>
      <th>Author:</th>
      <td><?php echo filter_navigation(objects_for_filter($authors), 'author_id', $author_id) ?></td>
    </tr>
    <tr>
      <th>Genre:</th>
      <td><?php echo filter_navigation(objects_for_filter($genres), 'genre_id', $genre_id) ?></td>
    </tr>
  </tbody>
</table>
<hr />
<table class="list">
  <thead>
    <tr>
      <th><?php link_to_unless($sort == 'id', 'Id', filter_url('sort', 'id')) ?></th>
      <th><?php link_to_unless($sort == 'title', 'Title', filter_url('sort', 'title')) ?></th>
      <th><?php link_to_unless($sort == 'author_id', 'Author', filter_url('sort', 'author_id')) ?></th>
      <th><?php link_to_unless($sort == 'genre_id', 'Genre', filter_url('sort', 'genre_id')) ?></th>
    </tr>
  </thead>
  <tbody>
    <?php foreach ($pager->getResults() as $book): ?>
      <tr>
        <td><?php echo $book->getId() ?></td>
        <td><?php echo $book->getTitle() ?></td>
        <td><?php echo $book->getAuthor()->getName() ?></td>
        <td><?php echo $book->getGenre()->getName() ?></td>
      </tr>
    <?php endforeach; ?>
  </tbody>
</table>
<?php echo pager_navigation($pager) ?>

NOTE: pager_navigation is covered at http://www.symfony-project.com/snippets/snippet/4, but needs to guess the uri, which is done at http://www.symfony-project.com/snippets/snippet/59.

NOTE: filter_navigation returns an unordered list, so you'll need css to display the <li> tags inline, and remove <ul> padding, margin, etc.

I'll leave it as an exercise to the reader to create the controller (action) code for this template, but it should be obvious.

The Code

<?php
 
/**
 * Generate a url using the current internal uri, but replaces a param with a new value.
 * If the param is not in the current uri's query string, it is added instead.
 *
 * This is useful for a page that uses several filters to record sets,
 * and needs all the filters to work together, instead of blasting each
 * other away when a new link is clicked.
 *
 * <strong>Examples:</strong>
 * <code>
 *  // with current uri => mymodule/myaction?author=10&genre=3
 *
 *  echo link_to('new author', filter_url('author', 5));
 *  // uri when clicked => mymodule/myaction?author=5&genre=3
 *
 *  echo link_to('new genre', filter_url('genre', 1));
 *  // uri when clicked => mymodule/myaction?author=10&genre=1
 *
 *  // with current uri => mymodule/myaction
 *
 *  echo link_to('an author', filter_url('author', 10));
 *  // uri when clicked => mymodule/myaction?author=10
 * </code>
 *
 * @param string the name of the parameter to replace
 * @param string the value to replace the current value with
 * @param boolean use route name
 * @return string the url with the parameter replaced
 * @see link_to
 */
function filter_url($param, $new_value, $with_route_name = false)
{
    // fetch params from query string
    $params = _get_params(_get_query_string());
 
    // replace param with new value
    $params[$param] = $new_value;
 
    return _get_uri($with_route_name) . '?' . _build_query_string($params);
}
 
/**
 * Removes a parameter from the current uri and returns the resulting url.
 *
 * @see filter_url
 */
function remove_filter_url($param, $with_route_name = false)
{
    // fetch params from query string
    $params = _get_params(_get_query_string());
 
    // remove param
    unset($params[$param]);
 
    return _get_uri($with_route_name) . '?' . _build_query_string($params);
}
 
/**
 * Generates an unordered list of links to filter the current record set by.
 * Multiple sets of filter_navigation links will work together, using the current uri.
 *
 * <strong>Examples:</strong>
 * <code>
 *  echo filter_navigation(array(10=>'Jones', 12=>'Smith, J.', 13=>'Darby'), 'author_id', 13);
 *  echo filter_navigation(objects_for_filter($authors), 'author_id', 13);
 * </code>
 *
 * @param array list of key=>value pairs of ids and strings
 * @param string the name of the parameter for this filter
 * @param string the selected id (or null, if none selected)
 * @param string the text to use for the "all" link
 * @see filter_url
 */
function filter_navigation($list, $param, $selected = null, $all_text = 'All')
{
    $html = '';
 
    $html .= content_tag('li', link_to_unless($selected === null, $all_text, remove_filter_url($param)));
    foreach ($list as $key => $value)
    {
        $html .= content_tag('li', link_to_unless($selected == $key, $value, filter_url($param, $key)));
    }
 
    return content_tag('ul', $html);
}
 
/**
 * Generates a simple list from a record set of propel objects.
 * Expects a getId function and a toString function.
 *
 * @param array objects to be converted to a list
 * @see filter_navigation
 */
function objects_for_filter($objects)
{
    $list = array();
 
    foreach ($objects as $object)
    {
        $list[$object->getId()] = $object->toString();
    }
 
    return $list;
}
 
function _get_uri($with_route_name = false)
{
    $internal_uri = sfRouting::getInstance()->getCurrentInternalUri($with_route_name);
    $ar = explode('?', $internal_uri);
 
    return ($with_route_name ? '@' : '') . $ar[0];
}
 
function _get_query_string()
{
    $internal_uri = sfRouting::getInstance()->getCurrentInternalUri();
    $ar = explode('?', $internal_uri);
 
    return isset($ar[1]) ? $ar[1] : '';
}
 
function _get_params($query_string)
{
    // parse query string into associative array
    $params = array();
    if ($query_string != '')
    {
        foreach (explode('&', $query_string) as $kvpair)
        {
            list($key, $value) = explode('=', $kvpair);
            $params[$key] = $value;
        }
    }
 
    return $params;
}
 
function _build_query_string($params)
{
    // build list of key=value strings
    $ar = array();
    foreach ($params as $key => $value)
    {
        $ar[] = $key . '=' . $value;
    }
 
    return implode('&', $ar);
}

NOTE: Place this in apps/myapp/lib/helper/FilterHelper.php.

Additional Usage Examples

<?php echo link_to('new author', filter_url('author_id', 10)) ?>
<?php echo url_for(filter_url('author_id', 10)) ?>
<?php echo link_to_if($condition, 'new author',  filter_url('author_id', 10)) ?>
<?php echo link_to_unless($condition, 'new author',  filter_url('author_id', 10)) ?>
<?php echo button_to('new author',  filter_url('author_id', 10)) ?>

Additional Helpers

<?php
 
/**
 * Shortcut combining link_to and filter_url into single function.
 *
 * @see link_to
 * @see filter_url
 */
function link_to_filter($name, $param, $new_value, $options = array())
{
    return link_to($name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining url_for and filter_url into single function.
 *
 * @see url_for
 * @see filter_url
 */
function filter_url_for($param, $new_value)
{
    return url_for(filter_url($param, $new_value));
}
 
/**
 * Shortcut combining link_to_if and filter_url into single function.
 *
 * @see link_to_if
 * @see filter_url
 */
function link_to_filter_if($condition, $name, $param, $new_value, $options = array())
{
    return link_to_if($condition, $name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining link_to_unless and filter_url into single function.
 *
 * @see link_to_unless
 * @see filter_url
 */
function link_to_filter_unless($condition, $name, $param, $new_value, $options = array())
{
    return link_to_unless($condition, $name, filter_url($param, $new_value), $options);
}
 
/**
 * Shortcut combining button_to and filter_url into single function.
 *
 * @see button_to
 * @see filter_url
 */
function button_to_filter($name, $param, $new_value, $options = array())
{
    return button_to($name, filter_url($param, $new_value), $options);
}
by Stephen Riesenberg on 2007-02-03, tagged css  filter  helper  pager  pagination  template  view 

ajax pagination

This class allows ajax request content to be easily paginated with desired visual effect.

<?php
/**
        @name               ajaxpager.class.php
        @desc               allows ajax pagination.
                            $pager:pager object created using sfPropelPager
                            $url:URL of desired action.(mostly current ajax action)
                            $divId:id of div to be updated after pagination
                            $params:extra parameters to send with pager (but not implemented yet.you can implement it by yourself)
                            $appear_effect:visual effect on completing the request.(default is 'Appear').You can use also 'Grow' or 'SlideDown' etc.
        @author             Ahmet ERTEK, erteka@gmail.com
        @copyright          DVS Bilisim, www.dvs-tr.com
        @version            1.0.0
 
*/
class ajaxpager
{
    private $pager;
    private $divId;
    private $url;
    private $params;
    private $appear_effect;
 
    /**
        @name               ajaxpager.class.php
        @desc               allows ajax pagination.
                            $pager:pager object created using sfPropelPager
                            $url:URL of desired action.(mostly current ajax action)
                            $divId:id of div to be updated after pagination
                            $params:extra parameters to send with pager (but not implemented yet.you can implement it by yourself)
                            $appear_effect:visual effect on completing the request.(default is 'Appear').You can use also 'Grow' or 'SlideDown' etc.
        @author             Ahmet ERTEK, erteka@gmail.com
        @copyright          DVS Bilisim, www.dvs-tr.com
        @version            1.0.0
 
*/
    public function ajaxpager($pager,$url,$divId,$params=null,$appear_effect='Appear')
    {
        $this->pager=$pager;
        $this->divId=$divId;
        $this->url=$url;
        $this->params=$params;
        $this->appear_effect=$appear_effect;
 
    }
/**
        @name               ajaxpager.class.php
        @desc               prints pagination.
        @author             Ahmet ERTEK, erteka@gmail.com
        @copyright          DVS Bilisim, www.dvs-tr.com
        @version            1.0.0
 
*/
    public function printPager()
    {
        $pager=$this->pager;
        $url=$this->url;
        $divId=$this->divId;
        $appear_effect=$this->appear_effect;
 
        if ($pager->haveToPaginate())
        {
            echo link_to_remote('«', array(
            'update' => $divId,
            'url'    => $url.'?page='.$pager->getFirstPage(),
            'complete'=>visual_effect($appear_effect, $divId),
            'loading'=>"$('$divId').innerHTML='<img src=/images/indicator.gif border=0>'",
            ), array('class'=>'contentLink'));
 
            link_to_remote('<', array(
            'update' => $divId,
            'url'    => $url.'?page='.$pager->getPreviousPage(),
            'complete'=>visual_effect($appear_effect, $divId),
            'loading'=>"$('$divId').innerHTML='<img src=/images/indicator.gif border=0>'",
            ), array('class'=>'contentLink'));
 
            $links = $pager->getLinks();
            foreach ($links as $page)
            {
                echo($page == $pager->getPage()) ? $page : link_to_remote($page, array(
                'update' => $divId,
                'url'    => $url.'?page='.$page,
                'complete'=>visual_effect($appear_effect,$divId),
                'loading'=>"$('$divId').innerHTML='<img src=/images/indicator.gif border=0>'",
                ), array('class'=>'contentLink'));
 
                if ($page != $pager->getCurrentMaxLink()){ echo "-"; }
            }
 
            echo link_to_remote('»', array(
            'update' => $divId,
            'url'    => $url.'?page='.$pager->getNextPage(),
            'complete'=>visual_effect($appear_effect, $divId),
            'loading'=>"$('$divId').innerHTML='<img src=/images/indicator.gif border=0>'",
            ), array('class'=>'contentLink'));
 
            link_to_remote('>', array(
            'update' => $divId,
            'url'    => $url.'?page='.$pager->getLastPage(),
            'complete'=>visual_effect($appear_effect, $divId),
            'loading'=>"$('$divId').innerHTML='<img src=/images/indicator.gif border=0>'",
            ), array('class'=>'contentLink'));
        }
    }
}
 
?>

Usage:

<?php
$ajax_pager=new ajaxpager($pager,'myDivId','account/pictures?id='.$accountId,null,'SlideDown');
$ajax_pager->printPager();
?>
by ahmet ertek on 2007-01-23, tagged pager  pagination 
(2 comments)

sfAdvancedPropelPager

I'm sure you've been there; you want to use Symfony's native sfPropelPager addon but you have to seed it with a complicated query that can't be reproduced via Criteria. In order to tackle this issue, I created a subclass of sfPropelPager called sfAdvancedPropelPager (it could probably benefit from a better name).

PLEASE NOTE: You will have to change all private methods and properties in sfPropelPager from 'private' to 'protected'. This won't break paging elsewhere in your projects, I promise you! What it will do is allow this subclass to gain access to it's parent's data and functionality. It should be noted that this addon was developed with the current stable version (0.6.3) of Symfony in mind.

I've cited the "search engine" example within the Askeet project as a suitable candidate for demonstration, albeit with some tweaks. See http://www.symfony-project.com/askeet/21 for details!!

<?php
...
public static function search($phrase, $exact = false, $offset = 0, $max = 10)
{
  $words    = array_values(myTools::stemPhrase($phrase));
  $nb_words = count($words);
 
  if (!$words)
  {
    return array();
  }
 
  $con = sfContext::getInstance()->getDatabaseConnection('propel');
 
  // define the base query
  $query = '
      SELECT DISTINCT '.SearchIndexPeer::QUESTION_ID.', COUNT(*) AS nb, SUM('.SearchIndexPeer::WEIGHT.') AS total_weight
      FROM '.SearchIndexPeer::TABLE_NAME;
 
  if (sfConfig::get('app_permanent_tag'))
  {
    $query .= '
      WHERE ';
  }
  else
  {
    $query .= '
      LEFT JOIN '.QuestionTagPeer::TABLE_NAME.' ON '.QuestionTagPeer::QUESTION_ID.' = '.SearchIndexPeer::QUESTION_ID.'
      WHERE '.QuestionTagPeer::NORMALIZED_TAG.' = ? AND ';
  }
 
  $query .= '
      ('.implode(' OR ', array_fill(0, $nb_words, SearchIndexPeer::WORD.' = ?')).')
      GROUP BY '.SearchIndexPeer::QUESTION_ID;
 
  // AND query?
  if ($exact)
  {
    $query .= '
      HAVING nb = '.$nb_words;
  }
 
  $query .= '
      ORDER BY nb DESC, total_weight DESC';
 
  // prepare the statement
  $stmt = $con->prepareStatement($query);
 
  $placeholder_offset = 1;
  if (sfConfig::get('app_permanent_tag'))
  {
    $stmt->setString(1, sfConfig::get('app_permanent_tag'));
    $placeholder_offset = 2;
  }
  for ($i = 0; $i < $nb_words; $i++)
  {
    $stmt->setString($i + $placeholder_offset, $words[$i]);
  }
 
  // time to call our shiny new pager!
  $pager = new sfAdvancedPropelPager('Question', $max);
  $pager->setStatement($stmt);
  $pager->setPage(ceil($offset / $max));
  $pager->init();
  return $pager;
}
...
?>

You would then assign the $pager to a suitable property in your action, and then handle your pagination in your template in the usual fashion as illustrated on <http://www.symfony-project.com/book/trunk/pager> - simple as that!

<?php
 
class sfAdvancedPropelPager extends sfPropelPager
{
  protected
    $statement = null;
 
  public function __construct($class, $defaultMaxPerPage = 10)
  {
    $this->setClass($class);
    $this->setMaxPerPage($defaultMaxPerPage);
    $this->setPage(1);
    $this->parameter_holder = new sfParameterHolder();
    $this->setPeerMethod('retrieveByPk');
  }  
 
  public function init()
  {
    require_once(sfConfig::get('sf_model_lib_dir').'/'.$this->getClassPeer().'.php');
 
    $rs = clone $this->getStatement()->executeQuery(ResultSet::FETCHMODE_NUM);
 
    $this->setNbResults($rs->getRecordCount());
 
    if (($this->getPage() == 0 || $this->getMaxPerPage() == 0))
    {
      $this->setLastPage(0);
    }
    else
    {
      $this->setLastPage(ceil($this->getNbResults() / $this->getMaxPerPage()));
      $this->statement->setOffset(($this->getPage() - 1) * $this->getMaxPerPage());
      $this->statement->setLimit($this->getMaxPerPage());
    }
  }
 
  public function getResults()
  {
    $rs = $this->getStatement()->executeQuery(ResultSet::FETCHMODE_NUM);
 
    $objects = array();
 
    while ($rs->next())
    {
      $objects[] = call_user_func(array($this->getClassPeer(), $this->getPeerMethod()), $rs->getInt(1));
    }
 
    return $objects;
  }
 
  protected function retrieveObject($offset)
  {
    $statement = clone $this->getStatement();
    $statement->setOffset($offset - 1);
    $statement->setLimit(1);
 
    $rs = $statement->executeQuery(ResultSet::FETCHMODE_NUM);
 
    $object = null;
    while ($rs->next())
    {
      $object = call_user_func(array($this->getClassPeer(), $this->getPeerMethod()), $rs->getInt(1));
    }
 
    return $object;
  }
 
  public function getStatement()
  {
    return $this->statement;
  }
 
  public function setStatement($stmt)
  {
    $this->statement = $stmt;
  }
}
 
?>
by Nick Winfield on 2006-12-01, tagged pager  pagination  propel 
(1 comment)

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 
(6 comments)

pager for custom SQL querys

I needed a one pager for custom SQL and i read the sfPropelPager and i change like this.

Howto use: write two SQL in Peer class like this.

   public function getReportsWithPager($params)
   {
 
         $WHERE = "FROM ".self::TABLE_NAME." WHERE ".self::SOMEWHEREFIELD." = '%s'";
 
          $SQL ="SELECT SUM(".self::FIELDNAME."), ".self::WHOIS." %s";    
          $cSQL = "SELECT COUNT(id) %s"  
          $WHERE = sprintf($WHERE, $params['where']);
          $SQL = sprintf($SQL, $WHERE);
          $cSQL = sprintf($cSQL, $WHERE);
 
         $pager = new myCustomPager($cSQL, $SQL, $params['maxpage']);
         $pager->setPage($params['page']);
 
         $pager->init();
         return $pager;          
   }

And call in your action:

       $this->pager = SomePeer::getReportsWithPager($params);

in Template,

     <?php 
     $list = $pager->getResults(); 
     while($list->next()):
     ?> 
     <div><?php echo  $list->get('sum'); ?></div>  
     <?php endwhile; ?>

And creation page links same with sfPropelPager, read the sfPropelPager.

<?php
 
/*
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 */
/**
 * @package    symfony
 * @subpackage addon
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 * @version    SVN: $Id: sfPropelPager.class.php 1415 2006-06-11 08:33:51Z fabien $
 */
 
/**
 *
 * myCustomPager class.
 *
 * @package    symfony
 * @subpackage addon
 * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
 *   
 * Changed: sfPropelPager.class.php 
 * Changed by: Timu EREN
 * Last Changed: 12 / 08 /2006(dd /mm/ YYYY)
 */
class myCustomPager
{
  private
    $page                   = 1,
    $maxPerPage             = 0,
    $lastPage               = 1,
    $nbResults              = 0,
    $query               = null,
    $countQuery     = null,
    $objects                = null,
    $cursor                 = 1,
    $parameters             = array(),
    $currentMaxLink         = 1;
 
 
  public function __construct($cSQL,$SQL, $defaultMaxPerPage = 10)
  {
        $con = Propel::getConnection();
        $SQL = $con->prepareStatement($SQL);
        $cSQL = $con->prepareStatement($cSQL);
        $this->setQuery($SQL);
        $this->setCountQuery($cSQL);
        $this->setMaxPerPage($defaultMaxPerPage);
        $this->setPage(1);
  }
 
  public function init()
  {
        $cForCount =  $this->getCountQuery();
        $c = $cForCount->executeQuery();
        $c->next();
 
        $this->setNbResults($c->get('count'));
 
        $c =  $this->getQuery();
        $c->setLimit(0);
        $c->setOffset(0);
        if (($this->getPage() == 0 || $this->getMaxPerPage() == 0))
        {
            $this->setLastPage(0);
        }
        else
        {
            $this->setLastPage(ceil($this->getNbResults() / $this->getMaxPerPage()));
            $c->setOffset(($this->getPage() - 1) * $this->getMaxPerPage());
            $c->setLimit($this->getMaxPerPage());
        }
  }
 
  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)
  {
    $cForRetrieve = $this->getQuery();
    $cForRetrieve->setOffset($offset - 1);
    $cForRetrieve->setLimit(1);
    $results = $cForRetrieve->executeQuery();
    return $results;
  }
 
  public function getResults()
  {
    $c = $this->getQuery();
    return $c->executeQuery();
  }
 
  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 getQuery()
  {
    return $this->query;
  }
 
  public function setQuery($c)
  {
    $this->query = $c;
  }
 
  public function getCountQuery()
  {
    return $this->countQuery;
  }
 
  public function setCountQuery($c)
  {
    $this->countQuery = $c;
  }
 
  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;
      }
    }
  }
}
 
?>
by Timu EREN on 2006-08-17, tagged pager  pagination  sql 
(4 comments)

Minimal CSS pagination helper

This is helper is mostly inspired by Pagination navigation helper, but there are some differences:

PaginationHelper.php

function pagination($pager)
{
    $uri = sfRouting :: getInstance()->getCurrentInternalUri();
    $html = '';
 
    if ($pager->haveToPaginate())
    {
        $uri .= strstr($uri, '?') ? '&page=' : '?page=';
 
        if ($pager->getPage() != 1)
        {
            $html .= '<li>' . link_to('first', $uri . '1') . '</li>';
            $html .= '<li>' . link_to('previous', $uri . $pager->getPreviousPage()) . '</li>';
        }
 
        foreach ($pager->getLinks() as $page)
        {
            if ($page == $pager->getPage())
                $html .= '<li><strong>' . link_to($page, $uri . $page) . '</strong></li>';
            else
                $html .= '<li>' . link_to($page, $uri . $page) . '</li>';
        }
 
        if ($pager->getPage() != $pager->getLastPage())
        {
            $html .= '<li>' . link_to('next', $uri . $pager->getNextPage()) . '</li>';
            $html .= '<li>' . link_to('last', $uri . $pager->getLastPage()) . '</li>';
        }
 
        $html = '<ul class="pagination">' . $html . '</ul>';
    }
 
    return $html;
}

Minimal CSS

ul.pagination li {
    display: inline;
    list-style-type: none;
    padding-right: 1em;
}

In your template

<?php echo use_helper('Pagination') ?>
<?php echo pagination($pager) ?>
by brikou on 2006-07-19, tagged css  helper  pager  pagination  propel 
(6 comments)

Alphabetical index

Suppose you have a list of items (products, contacts, etc) that you want to look up by name. You want them paginated by letter of the alphabet, not by a certain number of items on each page (the default pagination behavior). You will have a navigator that includes every letter of the alphabet, but only the letters that actually have items will be links. The current letter you are looking at will be highlighted. This is similar to a phone book.

Drawbacks

The drawback to using this method is that the entire contents of the table have to be loaded for each page in order to build the array of first letters for the navigator. Perhaps there would be a way to query the database for just the DISTINCT first letters of the LNAME field, in effect: SELECT DISTINCT substr(lname,1,1) FROM phonebook. If anyone knows how to go about that, please comment.

First, create your action in action.class.php:

public function executeList()
{
  $c = new Criteria();
  $c->addAscendingOrderByColumn(PhoneBook::LNAME);
  $entries = PhoneBook::doSelect($c);
 
  //now we need an array of all the first letters.  
  //At the same time we're looping, we'll put all the 
  //items we need for this page into a separate array
  $pageLetter = strtoupper($this->getRequestParameter('page','A'));
  $arrayOfFirstLetters = array();
  $thisList = array();
  $x=0;$y=0;
  foreach($entries as $entry)
  {
    $firstLetter = strtoupper(substr($entry->getLName(),0,1));
    $arrayOfFirstLetters[$x++]=$firstLetter;
    if($pageLetter==$firstLetter)
    {
      $thisList[$y++] = $entry;
    }
  }
  $arrayOfFirstLetters = array_unique($arrayOfFirstLetters);
  sort($arrayOfFirstLetters);
 
  //make the arrays available to the page template
  $this->page = $pageLetter;
  $this->pageLinks = $arrayOfFirstLetters;
  $this->names = $thisList;
}

And the template, listSuccess.php:

<?php include_partial('alphaPager',array('letters'=>$pageLinks,'thisPage'=>$page)) ?>
<table>
  <?php foreach ($names as $name): ?>
    <tr>
      <td>
        <?php echo link_to($name->getLName().', '.$name->getFName(), 'phonebook/show?id='.$name->getId()) ?>
      </td>
    </tr>
  <?php endforeach; ?>
</table>

And now the partial template, _alphaPager.php:

<div id="alphapager">
<?php
$x='A';
for($y=0;$y++<26;$x++)
{
  if($y>1)
  {
    echo ' | ';
  }
  if($x==$thisPage)
  {
    echo '<font class="alhpaHL">'.$x.'</font>';
  }
  else
  {
    if(array_search($x,$letters))
    {
      echo link_to($x,'phonebook/list?page='.$x);
    }
    else
    {
      echo $x;
    }
  }
}
?>
</div>
by Eric Heimerman on 2006-06-12, tagged alphabetical  pagination 
(2 comments)

How to get a propel pager working with a custom SQL query

The problem

Let us take the example of snipeet! :-) The function that selects snippets by tags is a custom SQL query. When filtering by tags there is no pager anymore.

Why is that? The problem is that sfPropelPaginate only works with a Criteria object, not with a raw query. As a result you cannot have pagination with raw SQL query which is a rather severe limitation.

The solution

The solution is to first encode the parameter of the query in the criteria (that's the tricky bit) and then to set up a custom peer method.

As i said earlier we will take a simplified version of snipeet as an example. We assume that we have a function that creates a sql statement from a list of tags.

Here we go about setting the dummy criteria that encodes an array of tags:

getCriteriaFromTags($tags)
{
  $c = new Criteria();
  for ($i = 1; $i <= count($tags); ++$i)
  {
    $prefix = 't'.$i;
    // note that that SnippetTagPeer::TABLE_NAME  SnippetTagPeer::NAME are in fact arbitrary
    // any other table and table.column combination would work as well
    $c->addAlias($prefix.SnippetTagPeer::TABLE_NAME, SnippetTagPeer::TABLE_NAME);
    $c->add($prefix.SnippetTagPeer::NAME, $tags[$i-1]);
  }
  return $c;
}

Now you can initialise a sfPropelPaginate object as usual with that Criteria object. When that is done you tell it to use your custom peer method:

$pager->setPeerMethod('doSelectByTags');

Next you code the doSelectByTags method that performs the actual selection from a criteria object:

function doSelectByTags($c)
{
  // first you fetch the tags from the query
  $tags = array();
  foreach($c->keys() as $key)
    $tags[] = $c->get($key);
 
  // here comes your custom sql query 
  // it basically creates a $statement variable from the $tags array
  // .....
 
  // now you have to add the limit and offset:
 $statement->setLimit($c->getLimit());
 $statement->setOffset($c->getOffset());
 // and the rest of the code is as usual
}
by Olivier Verdier on 2006-05-25, tagged custom  pager  pagination  propel  query  raw  sql 

Pagination navigation helper

In a template displaying a paginated list, you need to show the pager navigation. Create a PaginationHelper.php in lib/helper:

<?php
 
function pager_navigation($pager, $uri)
{
  $navigation = '';
 
  if ($pager->haveToPaginate())
  {  
    $uri .= (preg_match('/\?/', $uri) ? '&' : '?').'page=';
 
    // First and previous page
    if ($pager->getPage() != 1)
    {
      $navigation .= link_to(image_tag('/sf/images/sf_admin/first.png', 'align=absmiddle'), $uri.'1');
      $navigation .= link_to(image_tag('/sf/images/sf_admin/previous.png', 'align=absmiddle'), $uri.$pager->getPreviousPage()).' ';
    }
 
    // Pages one by one
    $links = array();
    foreach ($pager->getLinks() as $page)
    {
      $links[] = link_to_unless($page == $pager->getPage(), $page, $uri.$page);
    }
    $navigation .= join('  ', $links);
 
    // Next and last page
    if ($pager->getPage() != $pager->getLastPage())
    {
      $navigation .= ' '.link_to(image_tag('/sf/images/sf_admin/next.png', 'align=absmiddle'), $uri.$pager->getNextPage());
      $navigation .= link_to(image_tag('/sf/images/sf_admin/last.png', 'align=absmiddle'), $uri.$pager->getLastPage());
    }
 
  }
 
  return $navigation;
}

In your templates, display the pagination links like that:

<?php echo use_helper('Pagination') ?>
<?php echo pager_navigation($mypager, '@myrule') ?>
by Francois Zaninotto on 2006-05-20, tagged helper  pager  pagination  propel