Development

sfDoctrinePager

You must first sign up to be able to contribute.

Version 6 (modified by aarjona, 9 years ago)
--

sfDoctrinePager

A beginners guide on how to get it running.

What is paging?

If you have a long list of entries in a database and you split the list into smaller pieces, where you have for instance 10 on one page, then this is called paging. Additional, and very important in my opinion is the paging-navigation, that shows the user that there are several pages of information available. There are several approaches for the navigation possible (and maybe desirable - using AJAX or other techniques), but this is not subject of this article and covered elsewhere. This here is pure vanilla-approach, and you might fancy it up, once you get this done.

Prerequisites

  • a table (here named mytable) in a database (here named mydatabase)
  • lots of entries in your table - otherwise a pagination is not interesting...
  • be sure, that you use doctrine (and not propel) and thus, sfDoctrine should be installed.->See there.
  • understanding of the difference of some terms in Symfony (app, module, class...)

I assume also, that you already generated a model (myclass), an application (myapp), and a basic CRUD on that:

home$ symfony doctrine-generate-crud myapp mymodule myclass

Now, if you look at the running module http://myproject/myapp_dev.php/mymodule/list you will see that this is fine, as long as you want all the data on one page.

So you desire to split the list into smaller pieces, which can really easily be done.

Edit the action

The action which lists your table can be found in home/apps/myapp/modules/mymodule/actions/ and is called executeList() - somewhere in the beginning of the document, around line 25. One line creates almost the whole list. Almost? Well, the result of this line requires some others in the template listSuccess. At this point we hook the pager into the listing:

  1. Comment or delete this line.

  1. Insert the following:
    $this->pager = new sfDoctrinePager('myclass',10);
    $this->pager->getQuery();
    $this->pager->setPage($this->getRequestParameter('page',1));
    $this->pager->init();
    
  1. Replace 'myclass' with your real class-name in the first line of the pasted.

Description of the action code

Line 1

In the first line you create a new Object of the pager. You tell the object your class, and the desired length of a page (here 10). (Tip: If you only have 20 entries yet in mytable, then you can set this to 2).
For semi-pros: The desired 'length of the page' is the 'LIMIT' in the SQL-Statement.

Line 2

Here you prepare the query. You can include where- and order-statements, if you want. Leave it for the beginning. Believe me, it saves some trouble. Once you got the listing running, you can play around with. So beginners, skip this paragraph and read on in 'Line 3'.

Some examples:

$this->pager->getQuery()->from('myclass a')->where('a.myfield = ?', 'Searchstring');

'Searchstring' can of course be a variable you pass through.

One of the queries I use a lot and which results sometimes in long lists is:

$this->pager->getQuery()->from('myclass a')->where('a.myfield LIKE ?, 'Searchstring%');

The result is a list which maches all entries in myfield beginning with Searchstring.

For details about 'conditional expressions' like this one, you're welcome to visit the doctrine documentation, chapter 7.7, might be for a beginner a bit 'techie', but if you take a deep breath (or two) and encourage yourself you get used to that, and you will learn the phrases soon...

How to sort/order the result

I had some trouble with that firsthand. For a pure 'where' query you actually do not need a table alias (that is the 'a' you are defining in the from-Section). For an ordering it is necessary (don't ask me why!) to have that, otherwise it will throw errors at you.

An order-by -query looks like that:

$this->pager->getQuery()->from('myclass a')->orderBy('a.myfield DESC');

(the order can be either DESCending or ASCending).

And surely you can combine the where and order:

$this->pager->getQuery()->from('myclass a')->where('a.myfield = ?', 'Searchstring')->orderBy('a.myfield DESC');

Line 3

The page being displayed is set by setPage(pagenumber) (again for semi-pros: this computes the OFFSET in the SQL Statement). Since we don't want to alwasy just call 'page 1', we make that more flexible using a passed parameter 'page' which has a value. If this parameter is not passed we take the default page, which is '1'.

Line 4

Initialisation of the pager. If that passed, everything is in the pager object stored.

Visualisation

OK, so we are done with the action part. Now we need to display it in the display.
Open the template:

home/apps/myapp/modules/mymodule/templates/listSuccess.php

Somewhere in there is a line starting with <?php foreach (
Thats where we need to make a change. The default crud-generator writes $myclasss as $myclass - note the 'simulated' plural form of myclass by adding an 's' at the end. This term needs to be replaced by:

$pager->getResults() as $myclass

So the resulting line should look like

<?php foreach ($pager->getResults() as $myclass): ?>

note that you can use the much-faster fetch_array syntax with the pager, also:

<?php foreach ($pager->getResults('array') as $myclass): ?>

Give it a try and view the list in your browser.

Play around by calling other pages in adding to your URL page/2 etc.

Browsing your paginated result

Well, turning the pages that way is not the cutting edge of usability, right?

As always there are a lot of possibilities, and here I want to introduce you one of the simple but useful. More vanilla, less fudge.

The basic browsing functionality is:

First Page - previous Page - current Page - direct link to a couple next pages - next page - last Page

Actually quite a lot of hrefs to write, but the good news is, that with a Helper we only need one line! Well, to be honest, two.
The first line is 'embedding' the Helper in the top of the page, so that it is available for a call.

So place at the top of the template listSuccess.php the following line:

<?php use_helper('Pagination'); ?>

and then below the table (or wherever you want, thats a visual-design question)

<?php  echo pager_navigation($pager, '/mymodule/list/') ?>

And then you need to 'write' the Helper of course:

Create a file in

home/apps/myapp/lib/helper/PaginationHelper.php

And paste in the following code:

(btw. What I'm describing here is from http://www.symfony-project.com/snippets/snippet/4 written way back in 2006 by Francois Zaninotto. I adapted the code a little, since the location of the images has changed)

<?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/sf_admin/images/first.png', 'align=absmiddle'), $uri.'1');
      $navigation .= link_to(image_tag('/sf/sf_admin/images/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/sf_admin/images/next.png', 'align=absmiddle'), $uri.$pager->getNextPage());
      $navigation .= link_to(image_tag('/sf/sf_admin/images/last.png', 'align=absmiddle'), $uri.$pager->getLastPage());
    }
 
  }
 
  return $navigation;
}

Alright, thats it: Save and reload browser - and enjoy your paged huge list!

Imagine how long this might have taken if you wouldn't use a framework like symfony - So take the rest of the day off...

Further reading

The Pagination-navigation as described above: http://www.symfony-project.com/snippets/snippet/4

Pagination-navigation, with css - and a nice routing-automation, where you can skip the second parameter

http://www.symfony-project.com/snippets/snippet/59

And then of course the whole collection, just watch out that you're not shifting in a pure 'propel' environment.

http://www.symfony-project.com/snippets/snippets/tagged/pager+pagination/order_by/date

Thanks

for all the supporters who gave me input through the forum or #doctrine and #symfony
And of course to all the programmers who donated us symfony!

comments/complains to udowsky #symfony