Aujourd’hui, petit tuto facile : comment mettre en place un système de pagination avec Doctrine.
Comme exercice, et comme il est trop tard pour que mon pauvre cerveau fatigué soit capable d’originalité, nous allons classiquement afficher une liste paginée d’articles de blogs.
Je pars du principe que nous disposons déjà d’un projet Symfony initialisé et à peu près propre. Allons-y pour la définition du modèle et les données de test.
(Note : je sais que la coloration syntaxique ici est horrible. Si quelqu’un sait où trouver un thème couleu pour fond sombre, je suis preneur).
# config/doctrine/schema.yml
BlogPost:
actAs:
Sluggable:
fields: [title]
columns:
title: { type: string(255), notnull: true }
body: { type: string(5000), notnull: true }
# data/fixtures/fixtures.yml
# fixtures dynamiques. Classe, non ?
BlogPost:
<?php for ($i = 1; $i <= 50; $i++): ?>
post_<?php echo $i ?>:
title: mon article <?php echo $i ?>
body: |
ceci est le corps de l article <?php echo $i."\n" ?>Ok pour les données. Si ce n’est déjà fait, créons nous un petit module de travail :
symfony generate:module frontend post
Histoire de poser les bases, nous allons commencer par afficher la liste de tous les articles :
// apps/frontend/modules/post/actions/actions.class.php
// ...
public function executeList(sfWebRequest $request)
{
$this->posts = Doctrine::getTable('BlogPost')
->createQuery('p')
->execute();
}
// apps/frontend/modules/post/templates/listSuccess.php
<?php foreach($posts as $post): ?>
<div class="post">
<!-- La dedans, je vous laisse mettre ce que vous voulez -->
<?php include_partial('post_detail', array('post' => $post)) ?>
</div>
<?php endforeach ?>Joli liste, mais un peu longue, non ? Il est temps de mettre en place la pagination. Pour cela, nous allons utiliser l’objet sfDoctrinePager.
// apps/frontend/modules/post/actions/actions.class.php
// ...
public function executeList(sfWebRequest $request)
{
$q = Doctrine::getTable('BlogPost')
->createQuery('p');
// Combien de billets voulons nous afficher ?
$nbPosts = sfConfig::get('app_posts_number_per_page', 10);
// Quelle est le numéro de page à afficher
$numPage = $request->getParameter('page', 1);
$this->pager = new sfDoctrinePager('BlogPost', $nbPosts);
// Le pager prends en paramètre une requête doctrine.
$this->pager->setQuery($q);
$this->pager->setPage($numPage);
$this->pager->init();
}
// apps/frontend/modules/post/templates/listSuccess.php
<?php foreach($pager->getResults() as $post): ?>
<div class="post">
<!-- Pareil que plus haut. Débrouillez vous avec ça -->
<?php include_partial('post_detail', array('post' => $post)) ?>
</div>
<?php endforeach ?>
<?php if ($pager->haveToPaginate()): ?>
<div class="pagination">
<?php include_partial('paginate', array('pager' => $pager)) ?>
</div>
<?php endif ?>
<?php $routeName = sfContext::getInstance()->getRouting()->getCurrentInternalUri(false) ?>
<?php echo link_to('First', $routeName) ?>
<?php
if(strpos($routeName, '?') !== false)
$routeName .= "&";
else
$routeName .= "?";
?>
// apps/frontend/modules/post/templates/_paginate.php
<!-- Là, libre à vous d'utiliser un helper à la place -->
<?php $routeName = sfContext::getInstance()->getRouting()->getCurrentInternalUri(false) ?>
<?php echo link_to('First', $routeName.'page=1') ?>
<?php echo link_to('Previous', $routeName.'page='.$pager->getPreviousPage()) ?>
<?php foreach ($pager->getLinks() as $page): ?>
<?php if ($pager->getPage() == $page): ?>
<?php echo $page ?>
<?php else: ?>
<?php echo link_to($page, $routeName."page=$page") ?>
<?php endif; ?>
<?php endforeach; ?>
<?php echo link_to('Next', $routeName.'page='.$pager->getNextPage()) ?>
<?php echo link_to('Last', $routeName.'page='.$pager->getLastPage()) ?>Et voilà ! Vous remarquerez que l’object DoctrinePaginate nécessite qu’on lui passe un reqête Doctrine pour fonctionner. L’idéal sera de récupérer cet object requête via une fonction définie dans le modèle (Pourquoi résister à l’appel du MVC ?)
// apps/frontend/modules/post/actions/actions.class.php // ... public function executeList(sfWebRequest $request) { $q = Doctrine::getTable('BlogPost') ->getAllPostsSortedQuery(); // ... } // lib/model/doctrine/BlogPostTable.class.php // ... public function getAllPostsSortedQuery() { $q = $this->createQuery('p') ->orderBy('p.created_at DESC'); return $q; }
C’est tout. Amusez vous bien.

2 Commentaires
L’appel à sfContext::getInstance() dans un template c’est plutôt « moche » car ça n’appartient pas véritablement à la couche de la vue dans le modèle MVC. Préfère la variable $sf_context à la place ou bien passe ton objet de routing depuis ton action.
++
Que faut-il dans routing.yml pour faire fonctionner ceci ? J’ai des problèmes de routes qui ne matchent rien, je dois donc louper quelque chose.
Merci,
David