The symfony Cookbook

Comment paginer une liste

About

You are currently reading "The symfony Cookbook" which is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.

Symfony 2.0 Preview Release

Chapter Content

Apperçu général

L'objet sfPropelPager

Naviguer de pages en pages

Naviguer entre les objets

Changer l'order du tri

Modifier le nombre de résultats par page

Changer la méthode de sélection

Stocker des informations supplémentaires dans le paginateur

symfony training
Be trained by symfony experts
Mar 24: Paris (1.4 + Doctrine - Français)
Apr 12: Paris (What's new in 1.3/1.4 - Français)
Apr 21: Paris (1.4 + Doctrine - Français)
and more...

Search


powered by google
You are currently browsing "The symfony Cookbook" in French for the 1.1 version - Switch to version: - Switch to language:
Creative Commons License This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License.
Translation of this work into another language is explicitly allowed.
This version of symfony is not maintained anymore.
If some of your projects still use this version, consider upgrading as soon as possible.

Apperçu général

Symfony fournit un composant de pagination : l'objet sfPropelPager. Il est capable de séparer une liste de résultats issue d'un objet Criteria en un groupe de page à afficher, proposant les résultat par l'intermédiaire de méthodes.

L'objet sfPropelPager

La classe sfPropelPager utilise la couche d'abstraction de Propel, décrite au chapitre Modèle.

Le chapitre couvrira l'utilisation des méthodes de sfPropelPager à travers un example simple : Afficher une liste d'articles par bloc de 10. On suppose que l'objet Article a bien les méthodes d'accès getPublished(), getTitle(), getOverview() et getContent().

Si vous ne voulez que les résultats non-paginés d'une requête générée par Criteria selectionnant que les articles publiés, vous utilisez quelque chose du genre :

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

La variable $articles, disponible dans le template, contiendrait alors un tablea contenant tous les objets résultant de la requête.

Afin d'optenir unen liste paginée, une approche quelque peu différente est nécéssaire. En effet, les résultats doivent être stockés non plus dans un tableau, mais dans un objet sfPropelPager.

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;
    ...
  }
}

Les changements interviennent après la définition du critère, étant donné que cette action :

Le template listSuccess.php a maintenant accès à l'objet sfPropelPager. Cet objet connait la page courante et la liste de toute les pages. Il dispose de méthode d'accès au pages ainsi qu'aux objets. Voyons comment le manipuler.

Pour afficher le nombre total de résultats, utilisez la méthode getNbResults():

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

Pour afficher les articles dans une page demandée, utilisez la méthode getResults()de l'objet pager afin de récupérer les objets de la page:

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

Naviguer de pages en pages

Grâce à la méthode haveToPaginate(), l'objet de pagination sait si le nombre de résultats est supérieur au maximum affichable.

Pour ajouter les liens de navigation en bas de la liste (" < > "), utilisez les méthodes de navigation getFirstPage(), getPreviousPage(), getNextPage() et getLastPage(). La page courante est disponible par getPage(). Toute ces méthodes retournent un entier : l'indice de la page demandées.

Pour pointer sur page spécifique, boucler sur la collection de liens, obtenus grâce à la méthode getLinks().

<?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 ?>

Le rendu devrait être quelque chose du genre :

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

Une fois l'article affiché, un curseur est nécéssaire afin d'autoriser la navigation directe vers un article précédent ou suivant, sans retourner à la liste paginée.

Astuce: Le code ci-dessus est automatisé par le plugin sfPagerNavigation. Référez-vous à la [page de description]](http://www.symfony-project.com/trac/wiki/sfPagerNavigationPlugin) du plugin pour plus d'informations sur l'installation et l'utilisation.

Naviguer entre les objets

Naviguer page par page dans une liste est simple, mais l'utilisateur voudrait sûrement ne pas avoir à retourner dans la liste pour naviguer objet par objet. L'attribut curseur de l'objet sfPropelPager peut garder un indice de l'objet courant.

Cela permet un parcours article par article dans le template readSuccess.php. Modifions d'abord un peu de code dans le template listSuccess.php:

<?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 ?>

L'actin read aura besoin de savoir comment gérer le paramètre cursor.

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);
  }
}

La méthode getObjectByCursor($cursor) place le curseur à la position spécifiée et retourne l'objet à cette position précise.

Vous pouvez placer le curseur sans récupérer l'objet résultant avec la méthode setCursor($cursor). Une fois le curseur placé, vous pouvez récupérer l'objet à cette position (getCurrent()) mais égallement à sa position précédente (getPrevious()) ou suivante (getNext()).

L'action read sait donc passer au template les informations nécéssaires à une navigation article par article, en apportant toutefois quelques 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);
  }
}

Les méthodes getPrevious() et getNext() retournent null si aucun n'objet ne précède ou ne suit.

Voici une suggestion pour le template readSuccess.php:

<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()) ?>

Changer l'order du tri

Comme l'objet sfPropelPager est basé sur un objet Criteria, on peut facilement changer l'ordre du tri en appliquant un tri au critère avant de l'assigner à l'objet de pagination.

Par exemple, vous pouvez ajouter à l'interface de navigation le choix de la colonne de tri:

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;
    ...
  }
}

Ajoutez le code suivant au template listSuccess.php:

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

Modifier le nombre de résultats par page

La méthode setMaxPerPage($max) modifie le nombre de résultats affichable dans une page, sans recharger le paginateur (pas besoin de rapeller la méthode init()). Si le paramètre fourni est 0, le paginateur affichera tous les résultats dans la même 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;
    ...
  }
}

Vous pouvez donc ajouter au template listSuccess.php le code suivant:

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

Changer la méthode de sélection

Si vous avez besoin d'optimiser la performance d'une action basée sur sfPropelPager, vous aurez surement besoin d'utiliser une méthode doSelectJoinXXX() au lieu d'un simple doSelect(). Cela peut être facilement fait grâce à la méthode setPeerMethod() de l'objet sfPropelPager.

$pager->setPeerMethod('doSelectJoinUser');

Notez que le paginateur éxécute réellement la requête doSelect() à l'affichage de la page. La première requête (initié par $pager->init()) ne fait qu'un doCount(), qui est aussi personnalisable en appelant :

$pager->setPeerCountMethod('doCountJoinUser');

Stocker des informations supplémentaires dans le paginateur

Vous aurez sans doute besoin de garder un certain context dans l'objet de pagination. Pour ce besoin, la classe sfPropelPager peut gérer des paramètres de manière habituelle:

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

Ces paramètres ne sont jamais utilisé directement par le paginateur.

Pour plus d'informations sur les paramètres, référez vous au Chapitre 2.

Questions & Feedback

If you find a typo or an error, please register and open a ticket.

If you need support or have a technical question, please post to the official user mailing-list.

The Sensio Labs Network

Since 1998, Sensio Labs has been promoting the Open-Source software movement by providing quality web application development, training, consulting.
Sensio Labs also supports several large Open-Source projects.