Sneaky Drupal Pagers

See the update at the bottom!

Drupal’s pagers are neat, and when they were first developed, were way ahead of their time. They also have a couple problems. One of them is scalability. When you’ve got 10,000,000 somethings, calculating how many pages there are so that you can skip to the last one is time consuming.

Another limitation is that the pager is designed to page over a database query. The Apache Solr Search module uses Drupal pagers to move through pages of search results that come from Solr. The pseudo code for getting this to work looks like this:

<?php
// What result do we want to start on?
$offset = $page * $number_per_page;

// How many search results are there in total?
$total = $result->get_total();

// Send a very simple database query to the pager system to trick it.
pager_query("SELECT %d", $offset, 0, NULL, $total);

// Magic happens here. A pager appears.
$output .= theme('pager');
?>

That’s great! But I recently had a case where it was impossible to tell how many results there are in the total set. What is really needed is the ability to advance the pager until there aren’t any more pages, but Drupal doesn’t support anything like this by default. Twitter does it, but Drupal… meh. It’s sneaky time!

<?php
// Remember: http://is.gd/3Sf9Z
$total = 0;

// Note that $page is zero based (page zero is the first page).
// Note also that count($results) is just one page's worth of results,
// not the entire possible set (which is impossible to calculate).

// If there are fewer results than what we want to show per page,
// we know we've come to the end of the result set, and don't need
// to show any more pages.
if (count($results) < $number_per_page) {
 
$total = $number_per_page * ($page + 1);
}
// Otherwise, we want to tell the pager to give us yet another
// page to go to.
else {
 
$total = $number_per_page * ($page + 2);
}
// Now the pager will either end where we are, or add one
// more page to the end. This way you can keep advancing one
// more page until there are no more results left.
pager_query("SELECT %d", $number_per_page, 0, NULL, $total);
$output .= theme('pager');
?>

This strategy could be applied to both of the problem cases I mentioned above. If you have a HUGE result set and need a pager, and don’t want to destroy your database, this is a viable technique. It also works if you’re getting your results from a source that can’t tell you how many results there are in total. And it’s sneaky. Enjoy.

Update:

Instead of using a database query to manipulate the page you can manipulate the globals instead:
$GLOBALS[‘pager_page_array’][] = 1; //what page you are on
$GLOBALS[‘pager_total’][] = 3; // total number of pages
$items_per_page = 50;
print theme(‘pager’, NULL, $items_per_page);

Thanks Chx for the tip!

Comments

Sneaky?

Can you show how you use this snippet?
You could also set the $pager_* globals directly, thus avoiding unnecessary database queries in pager_query().
Obviously the pager needs a level of abstraction that allows to create pagers from other sources than database queries. That'd be sneaky. ;)

Great. but I do not know how

Great. but I do not know how to use.

I checked Drupal API, pager_query has only 4 Parameters but above example is 5.

pager_query($query, $limit = 10, $element = 0, $count_query = NULL)

thanks.

All further arguments to the function go to the query

You'll notice the query has a %d placeholder in it. The final argument goes to replace the placeholder.

-Rob

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Lines and paragraphs break automatically.

More information about formatting options