Embauchez-moi

Je suis freelance ! Besoin d'un professionnel du développement web ? Pourquoi ne pas me passer un coup de fil ?

Plus d'infos sur… http://thibault.jouannic.fr

mots-cles : Ingénieur web freelance Symfony eZ Publish Solr

La pagination avec Doctrine

Moleskine Hack
Creative Commons License photo credit : adotjdotsmith

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.


6 Commentaires

  1. Posté le 14/04/2009 à 21:33 | Permalien

    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.

    ++

  2. David L
    Posté le 03/12/2009 à 20:30 | Permalien

    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

  3. gaza
    Posté le 04/05/2010 à 18:36 | Permalien

    je voulais savoir comment récupérer les données indexer par doctrine pour les exploiter directement par zend lucène en vue de faire un moteur de recherche qui utilise les index générés par doctrine

  4. lefebvre nicolas
    Posté le 26/10/2010 à 15:30 | Permalien

    slt STP donne nous la la conf routing.yml

  5. Sylvainpierre
    Posté le 28/03/2011 à 14:35 | Permalien

    Merci pour ce cours.Mais dis-moi!c’est quoi la particularité de Doctrine ?

  6. Posté le 27/05/2011 à 16:07 | Permalien

    Doctrine est un ORM
    il permet de générer les requêtes SQL en objet.