Eclipse Pagination
Posted: Fri Jun 18, 2004 9:16 pm
Have been thinking: why not encapsulate pagination arithmetic in its own class ("Indexer")? The class might look like this:
For the next bit, a minor modification to standard iterators is required. Rather than resetting to the 0 row every time, "seeking iterators" can be reset to any row index by passing an $offset arg to the reset() method (see below).
Setting that aside for a moment, here's Indexer in use:
$indexer->getOffset() allows the iterator to be reset to the desired offset. $indexer->isValid() 'observes' the iteration, counting out each loop until the page limit is reached.
In this way PagedQuery & PagedQueryResult can be replaced with a smaller, simpler and more focussed Indexer class alongside the normal (almost..) iterators.
Indexer deals with all the pagination arithmetic and nothing else. In the end that's all pagination is: some simple arithmetic. Perhaps this is expressed more clearly in its own, dedicated class.
Seeking Iterators
QueryIterator modified as a "Seekerator":
Then refactor QueryResult->getRow to separate out seeking and fetching:
Code: Select all
/*
CLASS Indexer
The simple arithmetic required for pagination-type operations.
A (modified) iterator would seek to the getOffset() value.
isValid() would be called per iteration.
*/
class Indexer
{
var $i = 0;
/*
param (integer) $total_rows
param (integer) $subset_size ie max rows per page
*/
function Indexer($total_rows, $subset_size)
{
$this->total_rows = $total_rows;
$this->subset_size = $subset_size;
}
/*
param (integer)
return (integer)
*/
function getOffset($page_number = 1)
{
return ($this->_pageNumber($page_number) - 1) * $this->subset_size;
}
/*
Simple counter called once per iteration.
return (bool)
*/
function isValid()
{
return (++$this->i < $this->subset_size);
}
/*
In addition to its use in _pageNumber(), this method would also be used for pagination hyperlink creation & hence isn't "private".
return (integer)
*/
function getNumPages()
{
return ceil($this->total_rows/$this->subset_size);
}
//////////////////////////////////////////
// PRIVATE //
//////////////////////////////////////////
/*
Filter invalid page numbers.
return (integer)
*/
function _pageNumber($page_number)
{
if($page_number <= 0)
{
return 1;
} elseif($page_number > $num_pages = getNumPages()) {
return $num_pages;
} else {
return $page_number;
}
}
}Setting that aside for a moment, here's Indexer in use:
Code: Select all
/*
$it (object) - a "seeking" query iterator
$indexer (object) - instance of Indexer, above
$page (integer) - a page number
*/
for($it->reset($indexer->getOffset($page)); ($it->isValid() and $indexer->isValid()); $it->next())
{
// etc
}In this way PagedQuery & PagedQueryResult can be replaced with a smaller, simpler and more focussed Indexer class alongside the normal (almost..) iterators.
Indexer deals with all the pagination arithmetic and nothing else. In the end that's all pagination is: some simple arithmetic. Perhaps this is expressed more clearly in its own, dedicated class.
Seeking Iterators
QueryIterator modified as a "Seekerator":
Code: Select all
/*
param (integer) $offset
*/
function reset($offset = 0)
{
$this->query_result->seek($offset);
$this->index = $offset;
}Code: Select all
function getRow($index, $type = ECLIPSE_DB_BOTH)
{
$this->seek($index);
++$this->currentRow;
switch ($type)
{
case ECLIPSE_DB_ASSOC:
return mysql_fetch_assoc($this->getResultId());
case ECLIPSE_DB_NUM:
return mysql_fetch_row($this->getResultId());
case ECLIPSE_DB_BOTH:
default:
return mysql_fetch_array($this->getResultId());
}
}
function seek($index)
{
if ($index != $this->currentRow)
{
mysql_data_seek($this->getResultId(), $index);
$this->currentRow = $index;
}
}