Arborint and I have been hard at work on a pagination component that can be used standalone or as part of the Skeleton framework. We’re looking for feedback from other developers, so take a look and let us know what you think.
Executive Summary
We made these classes first and foremost to make it drop-dead easy to paginate data. Our goal was not to make a one size fits all solution, but rather to provide a layered range of solutions in which developers can pick and choose to tailor the classes to their needs.
Specific Goals
- Account for as many use cases as possible
- Stay flexible for extendability
- Take care of the messy details of pagination: url generation, maintaining state, dealing with the request.
Basics
The base object in this pagination system is a Value Object that holds the values for the list and does the necessary calculations. This class can be used alone, but you are required to initialize it.
$pager = new A_Pagination_Core($datasource);
$pager->setCurrentPage(intval($_GET['mypagevar']));
$pager->setNumItems(intval($_GET['mycountvar']));
This component also provides a class that extends the Core class and initializes itself from the request so you don’t have to. This class adds a process() method that will initialize the object for you:
$pager = new A_Pagination_Request($datasource);
$pager->process();
This Core object does the necessary calculation and will return page numbers for the current first, last, next and previous pages, plus a range of page numbers around the current page. These are intended to provide the basic functionality on which to build classes to render paginated output.
This class takes a Datasource object passed to the constructor. These Datasources comply to a simple Adapter interface. This component will provide Adapters for arrays, PDO, MySQL, etc. A getItems() method is provided to retrieve the records for the currently displayed page.
The basic methods in its interface are:
class A_Pagination_Core {
function __construct($datasource)
function isPage($number)
function getPage($number)
function getCurrentPage()
function getFirstPage()
function getLastPage()
function getPageRange()
function getNumItems()
function getItems()
}
This component is not monolithic. There are several levels of classes that use a Core object and provide support for rendering output. The lowest level of output support uses page numbers from a Core object and builds URLs. Using this class you can have complete control of your template:
$url = new A_Pagination_Helper_Url($pager);
$out .= ‘<a href=”‘ . $url->previous() . ‘”>Prev</> “;
$out .= ‘<a href=”‘ . $url->next(’Next’) . ‘”>Next</> “;
The next level of output support uses page numbers from a Core object and builds complete links (<a> tags). You an pass the name of a CSS class to this object to control link style:
$link = new A_Pagination_Helper_Link($pager);
$out .= $link->previous(’Prev’, ‘mylinkclass’);
$out .= $link->next(’Next’, ‘mylinkclass’);
Use Case Example
Here is an example of combining the classes to do a basic paginated list with links:
// create a data object that has the interface needed by the Pager object
$datasource = new Datasource();
// create a request processor to set pager from GET parameters
$pager = new A_Pagination_Request($datasource);
// initialize using values from $_GET
$pager->process();
// create a “standard” view object to create pagination links
include ‘A/Pagination/View/Standard.php’;
$view = new A_Pagination_View_Standard($pager);
// display the data
echo ‘<table border=”1″>’;
foreach ($pager->getItems() as $row) {
echo ‘<tr>’;
echo ‘<td>’ . $row['id'] . ‘</td><td>’ . $row['name'] . ‘</td>’;
echo ‘</tr>’;
}
echo ‘</table>’;
// display the pagination links
echo $view->render();
Additional Features
- There are a number of examples that show different ways the classes can be used — from do-it-yourself to standalone.
- There is support for ORDER BY functionality to sort the list. There are methods to generate column heading links to sort any column ascending/descending. Sort order is maintained while paging.
- The total number of items in the datasource is persisted RESTfully so, for example, COUNT() is only called one for database datasources.
- Request parameter names can be changed to remove conflicts and allow multiple pagers on one page.
- Pagination state values can be saved in the session if you want to be able to resume in the list where you left off — when editing records in CRUD for example.
Feedback Wanted
- Do these classes provide everything you need for pagination?
- What modifications would you make before using this in a project?
- What can we do to improve the code?
Discussions
This component is being discussed on two forums: SitePoint and the DevNetwork. Feel free to join in the discussion.
DataGrid
For the most automated use case, we will be providing a DataGrid that takes care of all HTML once the core classes have been finalized.