You are hereHome / Development / PHP Table Printer
PHP Table Printer
I recently introduced the PHP Printer Design Pattern and promised to talk about applying this pattern to tables and form. The printer pattern separates static and dynamic parts of a page and encapsulates the static part into a class, and I will do the same on tables now, coming up with a fully functional table printer class that even allows splitting up tables into several pages using only two lines of code.
First, let us examine what is needed to print a trivial table in PHP, by looking at a simple example:
<html>
<body>
<table>
<tr>
<th>Number</th>
<th>Count</th>
</tr>
<?php
class Data
{
var $number;
var $count;
}
// Fill an array of Data instances
$arrData = array();
for ($i = 0; $i < 13; $i++)
{
$temp = new Data();
$temp->number = $i + 1;
$temp->count = $i * 100;
$arrData[$i] = $temp;
}
// Loop through array and print rows.
foreach($arrData as $data)
{
?>
<tr>
<td><? print $data->number; ?></td>
<td><? print $data->count; ?></td>
</tr>
<?
}
?>
</table>
</body>
</html>
The only thing relevant is the content of the cells and the header, while both the table and tr tags together with the foreach loop are just necessary balast. Applying the printer pattern, I can divide this into a class handling the basic and repeating stuff and an interface handling the content.
Before doing this, however, I'd like to spend a few minutes talking about the foreach loop. While this is fine for arrays, what about a query result from the MySQL database? Looping is done differently here:
$queryID = mysql_query("Select * from Data");
while ( $record = mysql_fetch_array($queryID) )
{
?>
<tr>
<td><? print $record["number"]; ?></td>
<td><? print $record["count"]; ?></td>
</tr>
<?
}
Hmm. I prefer my table printer to handle this, too. So I'll abstract away the differences between arrays and query results using the well known concept of iterators. An iterator allows stepping through any kind of collection using the same interface. There is a nifty article about the iterator pattern and PHP 4 on the PHP Pattern Page, and you might want to have a look at it.
There are zillions of iterator implementations out there. What shall I say, here is mine. Both interface and implementation for arrays should be pretty clear, because they are straight forward.
Since itereators allow us to abstract away from the concrete data we process, everything's set to roll out the table printer class. As usual, the content handling is delegated to an interface:
/**
* Interface for classes to delegate row content output to
*/
class ITableRowPrinter
{
/**
* Prints the table header. <tr> tags are already processed by invoker
*
* @return void
*/
function printTableHeader($content)
{
}
/**
* Print one row. <tr> tags are already processed by invoker
*
* @return void
*/
function printTableRow($content)
{
}
/**
* Indicates if a table header should be printed
*
* @return Boolean TRUE if a header is printed, FALSE otherwise
*/
function hasTableHeader()
{
return true;
}
}
The simplest implementation for the printer class would be just one static function, that iterates through the data and invokes the according functions on the interface:
/**
* Printing a result table
*/
class TablePrinter
{
/**
* Static. Prints a table
*
* @param IIterator The iterator to use
* @param ITableRowPrinter The output class for the row content
*
* @return void
*/
function printTable($iterator, $rowPrinter)
{
print "<table>";
// Let's do header, if any
if ( $rowPrinter->hasTableHeader() )
{
if ( $dummy = $iterator->current() )
{
print "<tr>"
$rowPrinter->printTableHeader(&$dummy);
print "</tr>";
}
}
// Now print rows
while( $content = $iterator->current() )
{
print "<tr>";
$rowPrinter->printTableRow(&$content);
print "</tr>";
$iterator->next();
}
print "</table>";
}
}
Encapsulating all generic table code into one class has several advantages:
- It is easy to keep a consistent look and feel for all tables throughout an application
- Applicationwide changes are a snatch
- Following standards can be ensured by implementing them at one place. Always provoding summaries for tables, for example.
- Advanced features like different background colors for even and uneven rows or splitting tables across several pages must be implemented only once.
My current implementation, e.g. offers all this and needs just two lines to be invoked. Feel free to use it in your projects, too.
I created an examples page that demonstrates the usage of both page and table printers. You can browse the source code, too.
To be continued: Talking about security: applying the printer pattern to forms.
