Page One

As a community-supported open-source language, PHP enjoys the support of hundreds of dedicated developers, who are continuously adding to the available store of PHP libraries and knowledge. This support is most visible in projects like PECL, the PHP Extension Community Library and PEAR, the PHP Extension and Application Repository, which offers a robust, feature-rich collection of PHP widgets that can save developers a fair amount of time and effort.

One such widget is PEAR's Pager class, which offers developers a framework for breaking large data sets into smaller chunks, or pages, for greater readability or easier navigation. Pagination is important, particularly when dealing with result sets containing hundreds or thousands of items, because it allows the user to exert some degree of control over which segment of the data set is visible at any given point, and thus avoid drowning in a never-ending sea of data.

The interesting thing about pagination is that the program code needed to accomplish it is unlikely to change from project to project: once you've got it written and working to your satisfaction, you can recycle it for any other application that needs it. And using a ready-made Pager reduces the amount of code you have to write and test even further...and that's always a Good Thing!

Over the next few pages, I'm going to give you a quick run-down on the PEAR Pager class, together with a few examples of how you can integrate it with various Web applications while still looking calm and collected. Keep reading!

Superhero School

The Pager class is a PEAR package which provides a framework for PHP developers to add pagination and page-based navigation to large data sets. It's currently maintained by Lorenzo Alberton and Richard Heyes, and is freely available from http://pear.PHP.net/package/Pager.

The Pager class works by accepting an array of input values and then segmenting the array into discrete "pages" of smaller elements. It also generates navigation links to move back and forth between the pages. All aspects of both the paging mechanism and the appearance of the navigation links is configurable, as you'll see in the examples in this article.

Let's begin with a simple example, which demonstrates the Pager class in action. The data set used here is an array of super-hero names; this is purely illustrative and in future examples, you'll see both XML collections and SQL result sets used as the source data set.

<html>
 <head></head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// set data
$data = array('Superman', 'Spider-man', 'Batman', 'Robin', 'Green Lantern', 'Iron Man', 'The Flash', 'The Human Torch', 'The Hulk', 'Wolverine', 'Captain America', 'Spawn', 'Hellboy');

// set pager options
$params = array(
    'mode'     => 'Sliding',
    'perPage'  => 3,
    'delta'    => 1,
    'itemData' => $data
);

// generate pager object
$pager =& Pager::factory($params);

// get data for current page and print
$pageset = $pager->getPageData();
foreach ($pageset as $item) {
    echo $item . '<br/>';    
}

// get links for current page and print
$links = $pager->getLinks();
echo $links['all'];
?>
 </body>
</html>


If you view this script in your browser, you should see something like this:

Flip to the second page, and you'll see the page links at the bottom change as well:

This script begins by including the Pager class file, and initializing an array named $data with the complete set of values that need to be paged through. An instance of the Pager class is then created via the class' factory() method, which is passed an associative array of configuration parameters. Here's what each of them does:

  • The 'mode' parameter tells the Pager class whether the page links returned by the getLinks() method are to be displayed in "jumping" or "sliding" mode. In sliding mode, the set of page links returned by getLinks() is always centered around the current page; in jumping mode, the set of page links displayed depends on which page is current and the total number of pages. Personally, I use sliding mode for most applications, but feel free to be different.
  • The 'perPage' parameter indicates how many elements from the data set are to be displayed per page of data. In this example, 3 elements are displayed per page.
  • The 'itemData' parameter specifies the base data set to be paged through. In this example, it's the $data array of super-hero names.
  • The 'delta' parameter controls how many page numbers appear in the getLinks() output. It's closely linked to the value of the 'mode' parameter: in jumping mode, it specifies the total number of page links to show and, in sliding mode, it specifies the number of page links to show on each side of the current page number.

There are various other parameters as well - you'll see these a little further along in this tutorial.

Once an instance of the Pager class is initialized, the getPageData() method returns a subset of items for the current page, while the getLinks() method returns an array of links to the first, last, previous and next pages. This getLinks() method is important, because it automatically generates the navigation links between pages. For example, when you're viewing the first page, here's what getLinks() returns:

Array
(
    [0] => 
    [1] => <b><u>1&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=2" title="page 2">2</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;
    [2] => <a href="/pager_1.php?pageID=2" title="next page">»</a>
    [3] => 
    [4] => <a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [5] => <b><u>1&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=2" title="page 2">2</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=2" title="next page">»</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [6] => <link rel="next" href="/pager_1.php?pageID=2" title="next page" />
<link rel="last" href="/pager_1.php?pageID=5" title="last page" />

    [back] => 
    [pages] => <b><u>1&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=2" title="page 2">2</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;
    [next] => <a href="/pager_1.php?pageID=2" title="next page">»</a>
    [first] => 
    [last] => <a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [all] => <b><u>1&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=2" title="page 2">2</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=2" title="next page">»</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [linktags] => 

)


Notice the 'all' key of the returned array - it contains all the HTML code necessary to generate a page navigation bar. Similarly, the 'linktags' key contains the HTML code necessary to generate absolute (first and last) and relative (next and previous) page links.

And when you're viewing the second page, here's what getLinks() returns:

Array
(
    [0] => <a href="/pager_1.php?pageID=1" title="previous page">«</a>
    [1] => <a href="/pager_1.php?pageID=1" title="page 1">1</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<b><u>2&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;
    [2] => <a href="/pager_1.php?pageID=3" title="next page">»</a>
    [3] => <a href="/pager_1.php?pageID=1" title="first page">[1]</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    [4] => <a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [5] => <a href="/pager_1.php?pageID=1" title="first page">[1]</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=1" title="previous page">«</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=1" title="page 1">1</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<b><u>2&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="next page">»</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [6] => 
<link rel="previous" href="/pager_1.php?pageID=1" title="previous page" />
<link rel="next" href="/pager_1.php?pageID=3" title="next page" />
<link rel="last" href="/pager_1.php?pageID=5" title="last page" />

    [back] => <a href="/pager_1.php?pageID=1" title="previous page">«</a>
    [pages] => <a href="/pager_1.php?pageID=1" title="page 1">1</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<b><u>2&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;
    [next] => <a href="/pager_1.php?pageID=3" title="next page">»</a>
    [first] => <a href="/pager_1.php?pageID=1" title="first page">[1]</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    [last] => <a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [all] => <a href="/pager_1.php?pageID=1" title="first page">[1]</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=1" title="previous page">«</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=1" title="page 1">1</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<b><u>2&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="page 3">3</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=3" title="next page">»</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="/pager_1.php?pageID=5" title="last page">[5]</a>
    [linktags] => 



)


As you can see, getLinks() does all the hard work for you - it figures out which page you're viewing, how many pages there are in all (based on the total number of items and the items-per-page number), and automatically generates a set of links to let you move between pages. Pretty cool!

Now, update the script to change the mode from "sliding" to "jumping":

<html>
 <head></head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// set data
$data = array('Superman', 'Spider-man', 'Batman', 'Robin', 'Green Lantern', 'Iron Man', 'The Flash', 'The Human Torch', 'The Hulk', 'Wolverine', 'Captain America', 'Spawn', 'Hellboy');

// set pager options
$params = array(
    'mode'     => 'Jumping',
    'perPage'  => 3,
    'delta'    => 1,
    'itemData' => $data
);

// generate pager object
$pager =& Pager::factory($params);

// get data for current page and print
$pageset = $pager->getPageData();
foreach ($pageset as $item) {
    echo $item . '<br/>';    
}

// get links for current page and print
$links = $pager->getLinks();
echo $links['all'];
?>
 </body>
</html>


If you view this script in your browser, you should see something like this:

Flip to the second page, and you'll see the page links at the bottom change as well:

What's the difference? Well, in sliding mode, the 'delta' value of 1 ensured that the page numbers previous to and following the current page also appeared in the output of getLinks(). In jumping mode, the same setting restricted the total number of page numbers in the output of getLinks() to 1.

Bringing In The Database

The previous examples used a static array as the base data set for the Pager class. However, in the real world, you're more likely to be working with a dynamically-generated data set, such as that generated by a database query. It's in these situations that the Pager class truly comes into its own. Consider the following example, which pages through the result set returned by a query to the MySQL example 'world' database:


<html>
 <head></head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// connect to database
$conn = mysql_connect('localhost', 'user', 'pass') or die('ERROR: Unable to connect: ' . mysql_error());
mysql_select_db('world') or die('ERROR: Unable to find selected database');

// execute SQL
$query = 'SELECT Name FROM Country';
$rs = mysql_query($query) or die('ERROR: Unable to execute query: ' . mysql_error());

// place records in array
$data = array();
while ($value = mysql_fetch_row($rs)) {
    $data[] = $value[0];
}

// clean up MySQL connection
mysql_close($conn);

// set pager options
$params = array(
    'mode'     => 'Sliding',
    'perPage'  => 15,
    'delta'    => 3,
    'itemData' => $data
);

// generate pager object
$pager =& Pager::factory($params);

// get data for current page and print
$pageset = $pager->getPageData();
foreach ($pageset as $item) {
    echo $item . '<br/>';    
}

// get links for current page and print
$links = $pager->getLinks();
echo $links['all'];
?>
 </body>
</html>


No great magic here: the script opens a connection to the MySQL database server, retrieves a list of country names from the 'Country' table, iterates over the result set and adds each record to the $data array. Once this is complete, the Pager class is used to split the data into chunks for display, as in the previous examples.

Here's what it looks like:

Now, while the previous example works pretty well, one thing it isn't, is efficient. The reason? Every time a new page is viewed, the SQL query is executed anew and the entire set of database records is re-generated and placed in $data. Thus, although all the records in the table are retrieved each time a page is generated, only a small subset of the values are actually used at any given instant. And as the size of the database increases and the number of items per page decreases, the efficiency of the system above will decrease even further.

What's the solution? Well, one way to fix this is by using the 'pageID' variable, which is passed from page to page, to calculate the LIMIT clause for each page. Here's the code:

<html>
 <head></head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// set some variables
$perPage = 15;

// connect to database
$conn = mysql_connect('localhost', 'user', 'pass') or die('ERROR: Unable to connect: ' . mysql_error());
mysql_select_db('world') or die('ERROR: Unable to find selected database');

// get number of records in result set
$query = 'SELECT COUNT(*) FROM Country';
$rs = mysql_query($query) or die('ERROR: Unable to execute query: ' . mysql_error());
$row = mysql_fetch_row($rs);
$count = $row[0];
unset ($rs);

// now execute query with LIMIT clause:
// get current page number
// get record offsets for LIMIT clause
$currentPage = (!empty($_GET['pageID'])) ? mysql_escape_string($_GET['pageID']) : 1;
$startOffset = (int) ($currentPage-1) * $perPage;
$range = $perPage;

// execute query and get records
$limitQuery = 'SELECT Name FROM Country LIMIT ' . $startOffset . ', ' . $range;
$rs = mysql_query($limitQuery) or die('ERROR: Unable to execute query: ' . mysql_error());
$data = array();
while ($value = mysql_fetch_row($rs)) {
    $data[] = $value[0];
}

// clean up MySQL connection
mysql_close($conn);

// set pager options
$params = array(
    'mode'       => 'Sliding',
    'perPage'    => $perPage,
    'itemData'   => $data
);

// generate pager object
$pager =& Pager::factory($params);

// get data for current page and print
foreach ($pager->getPageData() as $item) {
    echo $item . '<br/>';    
}
unset($pager);

// set pager options
$params = array(
    'mode'       => 'Sliding',
    'perPage'    => $perPage,
    'totalItems' => $count,
    'delta'      => 4
);

// regenerate pager object
$pager =& Pager::factory($params);

// get links for current page and print
$links = $pager->getLinks();
echo $links['all'];
?>
 </body>
</html>


In this listing, a SELECT COUNT(*) query is first used to determine the total number of records in the result set, and this value is assigned to the $count variable. The value of the $pageID variable is then used to calculate the starting offset for the LIMIT clause, and a second SQL query is executed with this LIMIT clause, to return only the subset of records relevant for the current page. The Pager object is actually used twice in this listing: first to print the items in the result set returned by the second SELECT query, and then again to print the page links which appear at the bottom of the page.

While this isn't the most elegant solution to the problem - alternatives include the Pager_Wrapper class, discussed at http://www.alberton.info/pear_pager_tutorial_database_results.html, and the Structures_Datagrid class, discussed at http://pear.php.net/manual/en/package.structures.structures-datagrid.example-simple.php - it still works quite well, eliminating the performance drag seen in the first version of the listing by only retrieving those records which are relevant to the current page (rather than all the records in the database).

The XML Files

Why stop at paginating database results? Pager can also be used for other applications, such as paginating XML-encoded content. To illustrate, consider the following example XML document, which contains an article marked up with XML:

<?xml version='1.0'?>
<doc>
    <title>An Example Article</title>
    <abstract>An abstract? A short description. Is this short enough?</abstract>
    <page>
        This is the first page.    
        It can have multiple paragraphs. 
        Here's one.
        And here's another.
    </page>
    <page>
        <p>This is the second page.</p>    
    </page>
    <page>
        <p>This is the third page.</p>
        <p>There's one more to go.</p>    
    </page>
    <page>
        <p>This is the fourth and final page.</p>    
        <p>This is the end. Yippee!</p>    
    </page>
</doc>


In this XML file, each "page" of the article is enclosed within ... elements. With a little help from PHP's DOM functions, it's possible to extract all these elements, and pass them on to the Pager class. Take a look at some sample code:

<html>
 <head></head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// check for XML file
// load into DOM document if available
if (file_exists('article.xml')) {
    $doc = new DOMDocument;
    $doc->load('article.xml');
    
    // get all  elements
    // add each to $data array
    $pages = $doc->getElementsByTagName('page');
    for ($i = 0; $i < $pages->length; $i++) {
        $data[] = $pages->item($i);
    }    

    // set pager options
    $params = array(
        'mode'     => 'Sliding',
        'perPage'  => 1,
        'delta'    => 1,
        'itemData' => $data
    );
    
    // generate pager object
    $pager =& Pager::factory($params);
    
    // get content for current page and print
    // turn line breaks into HTML 
elements foreach ($pager->getPageData() as $p) { echo nl2br($p->nodeValue); } // get links for current page and print $links = $pager->getLinks(); echo $links['all']; } else { die('ERROR: Unable to find file'); } ?> </body> </html>


Here, PHP's DOM functions are used to extract a node collection of all the elements, and attach them to the $data array. PHP's Pager class is then used to iterate over these page by page - note the 'perPage' value set to 1 - and display the contents of each . As before, the getLinks() method takes care of producing links to move forward and backward between pages.

Here's an example of what the output looks like:

Looking Pretty

Pager also allows developers complete flexibility when it comes to customizing the navigation bar generated by the getLinks() method. This customization may be accomplished by passing various extra options to the Pager class' factory() method. Consider the following example, which illustrates:


<html>
 <head>
      
 </head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// set data
$data = range(1,1000);

// set pager options
$params = array(
    'mode'      => 'Sliding',
    'perPage'   => 15,
    'delta'     => 2,
    'itemData'  => $data,
    'altFirst'  => 'First page',
    'altPrev'   => 'Previous page',
    'altNext'   => 'Next page',
    'altLast'   => 'Last page',
    'separator' => '',
    'altPage'   => 'Page',
    'firstPageText' => '«',
    'lastPageText'  => '»',
    'prevImg'   => '‹',
    'nextImg'   => '›',
    'linkClass' => 'pager',
    'curPageLinkClassName'  => 'current',
    'spacesBeforeSeparator' => 2,
    'spacesAfterSeparator'  => 2,
);

// generate pager object
$pager =& Pager::factory($params);

// get data for current page and print
foreach ($pager->getPageData() as $item) {
    echo $item . ' <br /> ';    
}

// get links for current page and print
$links = $pager->getLinks();
echo $links['all'];
?>
 </body>
</html>


The array of configuration options passed to the Pager class' factory() method in this listing bears close examination:

  • The 'firstPageText' and 'lastPageText' keys specify the text that should appear instead of the first and last page numbers, while the 'prevImg' and 'nextImg' keys specify the text that should appear for the next and previous page links. HTML code can also be used in place of plain text for all these; this means that you can use <img /> tags (or any other HTML construct) to completely customize the appearance of the set of page navigation links.
  • The 'separator' key specifies the symbol to use between page links, while the 'spacesBeforeSeparator' and 'spacesAfterSeparator' keys specify the space before and after each separator. These keys are useful to control the space between each page link.
  • The 'linkClass' key specifies the name of the CSS class used to style each page link. In this example, this class is named 'pager', and it's responsible for setting the background colour (yellow) and padding (4px) around each page link. Similarly, the 'curPageLinkClassName' specifies the name of the CSS class used to style the page number corresponding to the current page.
  • The 'altFirst', 'altLast', 'altNext' and 'altPrev' keys specify alternate text to display for the first, last, next and previous page numbers. It's generally a good idea to include these keys so that users with older browsers can still see the navigation links.

Here's what the output looks like:

Of Form And Function

The Pager class also comes with some useful HTML widgets, which make it possible to replace or enhance the standard set of page links with some additional tools. The first of these is a drop-down list box that provides the user with a shortcut method to quickly select another page; this is useful when there are a lot of pages to get through and it isn't possible to display links to all of them due to space or other constraints.

Consider the following example, which illustrates:

<html>
 <head></head>
 <body> 
<?php
// include class
include_once 'Pager/Pager.php';

// set data
$data = range(1,1000);

// set pager options
$params = array(
    'mode'      => 'Sliding',
    'perPage'   => 15,
    'delta'     => 2,
    'itemData'  => $data,
);

// generate pager object
$pager =& Pager::factory($params);

// generate page link box
$options = array(
    'autoSubmit' => true,
    'optionText' => 'Go to page %d'
);
echo '
' . $pager->getPageSelectBox($options) . '
'; echo '<br />'; // get data for current page and print foreach ($pager->getPageData() as $item) { echo $item . ' <br /> '; } ?> </body> </html>


Here, the getPageSelectBox() method generates an HTML select box, which contains a list of all the page numbers for the current Pager. Selecting any of these immediately jumps to the corresponding page. Notice the two options passed to the getPageSelectBox() method: the 'autoSubmit' option automatically submits the form when it changes, while the 'optionText' option specifies the text that appears in each item of the selection list (%d is a placeholder for the page number).

Here's what it looks like:

You can also allow users to directly control how many items are displayed per page, with yet another widget: the getPerPageSelectBox(). Take a look:

<?php
// include class
include_once 'Pager/Pager.php';

// set data
$data = range(1,1000);

// set pager options
$params = array(
    'mode'        => 'Sliding',
    'perPage'     => 15,
    'delta'       => 2,
    'itemData'    => $data,
);

// generate pager object
$pager =& Pager::factory($params);
?>
<html>
 <head></head>
 <body> 
  <form action="<?php echo htmlentities($_SERVER['<?php_SELF']); ?>" method="get">
  <br />
  Jump to page:
  <br />
  <?php echo $pager->getPageSelectBox(); ?>
  
  <br />  

  Items per page:
  <br />
  <?php echo $pager->getPerPageSelectBox(25, 100, 25, false); ?>
  <br />
  <input type="submit" value="submit" />
  </form>
<?php
// get data for current page and print
foreach ($pager->getPageData() as $item) {
    echo $item . ' <br /> ';    
}

?>
 </body>
</html>


The first three parameters passed to the getPerPageSelectBox() method all relate to the values displayed in the output list: the starting value, the ending value, and the step value. In this example, the list box will allow the user to choose to display 25, 50, 75 or 100 items per page.

Notice also that in this example, the 'autoSubmit' option passed to the getPageSelectBox() is omitted, and both list boxes are placed within the domain of the same form. This is deliberately done so that both inputs - the items per page and the page number - are submitted back to the same form controller and used by Pager to re-render the set of page items.

Here's what it looks like:

Searching For Atlantis

As a final example, let's use the widgets demonstrated on the previous page, and everything else you've learned about Pager thus far, to generate a search results page which includes paging support. The basic application here will be a simple search form, which searches the 'Country' table of the MySQL 'world' database for all the countries matching a user-supplied search term and then generates a result set which the user can page through:

<html>
 <head></head>
 <body>
<?php
// include class
include_once 'Pager/Pager.php';

// connect to database
$conn = mysql_connect('localhost', 'user', 'pass') or die('ERROR: Unable to connect: ' . mysql_error());
mysql_select_db('world') or die('ERROR: Unable to find selected database');

// execute search query
$q = (isset($_GET['q'])) ? mysql_escape_string($_GET['q']) : '';
$query = "SELECT Name FROM Country WHERE Name LIKE '%$q%'";
$rs = mysql_query($query) or die('ERROR: Unable to execute query: ' . mysql_error());

// place records in array
$data = array();
while ($value = mysql_fetch_row($rs)) {
    $data[] = $value[0];
}

// clean up MySQL connection
mysql_close($conn);

// set pager options
// pass the query back in the GET string
$params = array(
    'mode'      => 'Sliding',
    'perPage'   => 10,
    'delta'     => 3,
    'itemData'  => $data,
    'extraVars' => array('q' => $q)
);

// generate pager object
$pager =& Pager::factory($params);

// generate form with various control boxes
?>
  <form action="<?php echo htmlentities($_SERVER['<?php_SELF']); ?>" method="get">
   Search for country: <input type="text" name="q" size="25" value="<?php echo $q; ?>" />
   Jump to page:  <?php echo $pager->getPageSelectBox(); ?>
   Results per page:  <?php echo $pager->getPerPageSelectBox(20, 100, 20, false); ?>
   <input type="submit" />
  
  
<?php
// get data for current page and print
$pageset = $pager->getPageData();
foreach ($pageset as $item) {
    echo $item . '<br/>';    
}

// get links for current page and print
$links = $pager->getLinks();
echo $links['all'];
?>
 </body>
</html>


Here's what it looks like:

The HTML form in this listing has three components: a text input box which accepts a search term, a list box which allows the user to jump to different pages of the result set (generated by getPageSelectBox()), and a second list box which allows the user to select how many result items appear on each page (generated by getPerPageSelectBox()).

The key to this listing, however, lies in the 'extraVars' option included in the Pager configuration array. This option specifies a list of extra variables to be automatically appended to the GET query string, as an array of key-value pairs. In the listing above, this facility is used to transfer the search term entered by the user, $_GET['q'], from one page to another.

Note also that you can make this script more efficient by adding LIMIT clauses to the SELECT query to reduce the number of items returned in each call to the page, using a technique outlined earlier; I've omitted those steps here to make the listing easier to understand.

And that's about it for this tutorial. Over the last few pages, I took you on a whirlwind tour of the Pager class, one of the more useful items in the PEAR collection. I showed you the basics of paging data sets, using both static data lists, database result sets and XML node collections. I also showed you how to customize the page links generated by the package, and how to use built-in HTML form widgets to control both the current page and the items displayed per page. Finally, I described one way in which you could use Pager to page through search results generated by a user query.

I hope you enjoyed this tutorial, and that it will save you some time the next time you sit down to code a paging system for a Web application. Happy coding!

Copyright Melonfire 2007, all rights reserved