A Challenge

How much effort does it take to display the results of a query in HTML? More specifically, how many lines of code?

Recently, Jason has done an amazing job of putting together a Data to Table tag for WACT.

You can see some examples of what it does here

It’s that good that I’m foolhardy enough to bet you can’t beat it in this challenge…

The Challenge

For the sake of bragging rights or just keeping ourselves amused; using any programming language you like and any public domain (i.e. available for download) libraries you wish, try producing the same results as the following three examples, with less lines of code.

I know number of lines of code isn’t necessarily the best measure of the value of code but it became one of the main issues in the J2EE vs. .NET Petstore shootout so I figure we can follow the lead.

To set the scene, the table should be displayed in an HTML layout like;


'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>



The Table Challenge



That tag is where your table should appear (feel free to remove it – it’s another WACT tag there to make my life easier).

The example.css file mentioned above looks like;


html, body {
font-family: georgia, serif;
}

/**
Table related
*/
.data {
color: blue;
background-color: silver;
border: ridge silver;
}
.data tr {
vertical-align: top;
}

/* Real browsers only... */
.data tr:hover {
background-color: yellow;
}

.data th {
color: black;
background-color: white;
font-weight: bold;
}
.data td {
font-size: 80%;
}
.even {
background-color: Bisque;
}
.odd {
background-color: Silver;
}
.deprecated {
background-color: DimGrey;
}

/**
Pager related
*/
.pager {
font-size: 80%;
color: red;
}
.pager a:link {
color: black;
}
.pager a:visited {
color: navy;
}
.pager a:hover {
background-color: yellow;
}

We won’t count the lines of code in the above HTML or CSS.

The database schema (plus sample data) for the table “phpmodules” used in the examples can be found here.

Example 1: A Simple Table

Kicking off, this example simply shows all columns in the phpmdules table;

The PHP Script (6 lines);


require '/home/hfuecks/wact/framework/common.inc.php';
require WACT_ROOT . '/template/template.inc.php';
require WACT_ROOT . '/db/db.inc.php';
$Page =& new Template('/example1.html');
$Page->setChildDataSource('MyTable',DBC::NewRecordSet('SELECT * FROM phpmodules'));
$Page->display();
?>

The template (example1.html: 2 lines);



Total Lines: 8

Here’s what it looks like:

Here’s a snapshot of the output HTML (whitespace formatted for readability);

The Table Challenge

Example 2: A Little Formatting

The first example displays a little too much information. First I want to hide the “Id”, “Url” and “Deprecated”. I also want to have the name of the PHP module link to the page in the PHP manual. Meanwhile the rows should be displayed with alternating row colors, to make it easier to read. And if a module is deprecated, I want a different, darker, row color to indicate that.

The PHP Script (19 lines);


require '/home/hfuecks/wact/framework/common.inc.php';
require WACT_ROOT . '/template/template.inc.php';
require WACT_ROOT . '/db/db.inc.php';
class RowColorFilter {
function doFilter(&$tpl, &$row, $rowNum) {
if ( $row->get('Deprecated') == 'Y' ) {
return 'deprecated';
}
if ( ($rowNum % 2) == 0 ) {
return 'even';
}
return 'odd';
}
}
$Page =& new Template('/example2.html');
$Table = & $Page->getChild('MyTable');
$Table->registerDataSet(DBC::NewRecordSet('SELECT * FROM phpmodules'));
$Table->registerRowCssClassFilter(new RowColorFilter());
$Page->display();
?>

The Template (9 lines):





{$Name}




Total Lines: 28

OK it’s crept up a bit, mainly thanks to the RowColorFilter class I introduced but such is the price of meeting user requirements.

Here’s how it looks:

And a snippet of the generated HTML;

The Table Challenge

Id Name Description Url Configuration Deprecated
1 Apache-specific Functions These functions are only available when running PHP as an Apache 1.x module. http://www.php.net/manual/en/ref.apache.php
2 Array Functions These functions allow you to interact with and manipulate arrays in various ways. http://www.php.net/manual/en/ref.array.php
3 Aspell functions The aspell() functions allows you to check the spelling on a word and offer suggestions. http://www.php.net/manual/en/ref.aspell.php --enable-mailparse Y

Example 3: Paged Result Set

Now we get to the knock out… right now the table displays all the rows at once; not a good idea for load on the database plus hard work scrolling down for users. It definately needs a “pager” so we only see something like 10 rows per page.

The PHP Script (20 Lines):
(Only needed to change two lines of code…)


require '/home/hfuecks/wact/framework/common.inc.php';
require WACT_ROOT . '/template/template.inc.php';
require WACT_ROOT . '/db/db.inc.php';
class RowColorFilter {
function doFilter(&$tpl, &$row, $rowNum) {
if ( $row->get('Deprecated') == 'Y' ) {
return 'deprecated';
}
if ( ($rowNum % 2) == 0 ) {
return 'even';
}
return 'odd';
}
}
$Page =& new Template('/example3.html');
$Pager = & $Page->getChild('ResultPager');
$Table = & $Page->getChild('MyTable');
$Table->registerDataSet(DBC::NewPagedRecordSet('SELECT * FROM phpmodules',$Pager));
$Table->registerRowCssClassFilter(new RowColorFilter());
$Page->display();
?>

The Template (22 lines):



First Prev ... Next Last


{$Name}




The lines of code in the template have jumped here to provide a fully customizable “pager”.

Total Lines: 42

You can now see the pager sat right above the table:

The result set pager is “Google style” (has First, Prev, Next, Last links plus links to individual pages).

Some of the generated HTML;

The Table Challenge

First
Prev
1 2
3
4
5
6

Next
Last

Name Description Configuration
Apache-specific Functions These functions are only available when running PHP as an Apache 1.x module.
Array Functions These functions allow you to interact with and manipulate arrays in various ways.
Aspell functions The aspell() functions allows you to check the spelling on a word and offer suggestions. --enable-mailparse

So that’s the challenge.

Example 1: 8 lines

Example 2: 28 lines

Example 3: 42 lines

Will be interested to see if anyone can beat it (or can be bothered).

And even if you can beat it on lines of code, will there still be a clean separation between application logic and presentation? With the above eamples you could give just hand the CSS and templates to a designer and they’d be able to radically alter the look and feel without touching any PHP.

You may also have a hard time beating these examples on performance…

Note there’s still a few things you can’t yet do with the data table tag, most editable cells (input text fields). WACT still has some fundamental changes to undergo before that’s a reality, but it’s not too far away now.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.php-tools.de schst

    Could you take a look at this:

    http://examples.php-tools.net/patTemplate/examples/_viewExample.php?example=example_realworld_paginate

    Alternating lines is possible without changing the PHP code. It may not be the same functionality as WACT, but I think, it’s nice, too. Creatug a table where teh amount of columns is flexible, is possible, too:
    http://examples.php-tools.net/patTemplate/examples/_viewExample.php?example=example_realworld_table

    Keep up the good work at WACT,

    Stephan

  • http://webtech.lv kaklz

    Not always the shortest code is the best one ;)

  • http://www.hostetler-family.net/mike/ escape164

    I am putting the finishing touches on a system that simply reads a database table, outputs all of the required functions, classes, templates and code to produce a very similar table.

    So, I guess the lines of code I use to generate my paged, searchable table would be #php -q runCodeGenScripts.php :-).

    Hehe, just had to throw that in there.

    I’ve been coding stuff like this for a while and I have been very impressed with WACT and I fully admit that their code is probably better than mine. Whenever I am faced with a decision to make my libs using design patterns or “understandable patterns” (mainly for future devs), I go with the latter.

    Congrats to Harry & the team for making such awesome code. They set the bar high for the rest of us!

  • jbardin

    What lines are you counting? Only the lines in the executing .php file? Obviously there is more to your table code than the 42 lines of ‘instance’ code. So I would assume you mean lines of code outside of a class definition (that would seem fair, but still has its tricks)? I will have you beat by the end of the day ;)

  • josheli

    i think the lines of code challenge is a bit disengenous. i could create a “library” for this particular problem, then all i would have to do in my client code is:

    < ?php
    include ‘challenge_lib.php’;
    echo challengeTable(‘challenge1′);
    ?>

    what does that prove? not much, other than someone coded a lot of backend. in my experience, libraries/frameworks like this are very good at easy to mildly difficult problems. it’s when you get into that last 25% of problems, the domain-specific stuff, that they tend to break down and you have to hack around it to make it do what you want.

  • Joe W.

    Well, the 22 lines in there that concern me the most are the lines in the Template. I don’t know this code. It’s not HTML. It’s not a flavor of XML that I’m familiar with. It’s not one or two tags (this is a block to repeat or not display) mixed in with my HTML. I gotta say that it seems like it will difficult to customize this template so that it looks like each of the sites I maintain. But with HTML templates, it’s easy.

  • jbardin

    This is just 1 method from my SiG.Sqlite object class (available from http://hsc.usf.edu/~jbardin/). It uses my own ‘html output library’ (look in SiG.Strings.php). run.php is the main file, this code should run just about anywhere (you will need to make 2 directorys ‘artifacts’ and ‘tmp’ that are writable by PHP in the folder that you put the code from the .zip file (or edit the code to another path and make the folders there). IMHO since I am the one writing the code I am the one who gets to decide what its structured output is (fuck the designers, they can play with CSS/dhtml if they want to change anything). Remeber people your data is only as good as its structure (read up on semantic web etc etc). Also I abide strictly to “Form follows Function”. Also it would be trivial to replace the ‘xmlstring’ classes with something that talked to say PDF or PHP-GTK. I have ommited the rest of the SiG.Sqlite class for readability (and this method is the one that does most of the magic anyhoo).


    //Take a table from the database this
    //object's instance is connected to and
    //turn it into an html table
    //(with a form for edits)

    function tableToHtml ($name)
    {
    $query = 'SELECT * FROM '.$name.' LIMIT 10';
    $result = $this->db->query($query);

    for ($i=0; $i< $result->numFields(); $i++) {
    $fields[] = $field = $result->fieldName($i);
    $headerTds[] = new Td(
    new Colspan('2'),
    new CDATA($field));
    }

    $headerTr = new Tr(
    new Style('background-color: yellow;'), $headerTds);

    $ii=0;
    foreach ($result as $row) {
    $dataTds = NULL;
    foreach ($fields as $field) {
    $dataTds[] = new Td(
    new CDATA($row[$field]));
    if (strstr($row[$field], "n")) { //actuall form element types should be
    //determined by field name, this was
    //just a quick hack to demonstrate
    //how many different things you can
    //plug into my XmlString objects
    $dataTds[] = new Td(
    new Textarea(
    new CDATA(htmlspecialchars($row[$field]))));
    } else {
    $dataTds[] = new Td(
    new Input(
    new Type('text'),
    new Size(strlen($row[$field])),
    new Value($row[$field])));
    }
    }

    $dataTrs[] = new Tr(
    new Style('background-color: '.(($ii++ % 2) ? 'tan' : 'gray').';'),
    $dataTds);
    }

    $actionTable = new Table(
    new Tr(
    new Td(
    new CDATA('Actions')
    ),
    new Td(
    new Input(new Type('submit'), new Value('Save'))
    )
    )
    );

    $dataTable = new Table(new Style('border: 1px solid black;'), $headerTr, $dataTrs);

    $form = New Form(
    new Action('run.php'),
    $dataTable,
    $actionTable);

    return $form->__toString();
    }

Name Description Configuration
Apache-specific Functions These functions are only available when running PHP as an Apache 1.x module.
Array Functions These functions allow you to interact with and manipulate arrays in various ways.
cid oid
3 109
3 136
3 152
4 19
4 56
4 28
5 117
5 72
5 104
5 151
Actions

  • http://www.sample.com Widow Maker

    > … fuck …

    Mind the language aye ? You can make a point all the same :)

    You could have used this for example,

    f***

  • texdc

    if you’re going to practice making tables, why not practice making them accessible as well?
    Just follow this format:


    A Table Heading
    Header One Header Two
    Foo Bar

    This way screen readers will read the table in a more coherant and comprehensible way. If there are any elements that you don’t want to see in the browser, just use CSS (media=”screen”) to hide them from the HTML output.

    Peace.

  • texdc

    Eh, why isn’t BBCode working? (Sorry!)

  • texdc

    One last time. Sorry to be a nusiance. It’s only a suggestion.


    A Table Heading
    Header One Header Two
    Foo Bar

  • http://www.phppatterns.com HarryF

    Not always the shortest code is the best one ;)

    Very true.

    i think the lines of code challenge is a bit disengenous. i could create a “library” for this particular problem, then all i would have to do in my client code is:

    < ?php include 'challenge_lib.php'; echo challengeTable('challenge1'); ?>

    You’re right – it’s not a highly specified challenge but that was the reason for saying “using a public domain library”, the principle being that any public domain library will general purpose rather than specific to my exact requirements. The stuff Jasons done with is very flexible as you can see with examples like this which uses functionality I didn’t even touch here (see template).

    Well, the 22 lines in there that concern me the most are the lines in the Template. I don’t know this code. It’s not HTML. It’s not a flavor of XML that I’m familiar with. It’s not one or two tags (this is a block to repeat or not display) mixed in with my HTML. I gotta say that it seems like it will difficult to customize this template so that it looks like each of the sites I maintain. But with HTML templates, it’s easy.

    This is “extended HTML” – something parallel to JSP Tag Libs or ASP.NET. The idea is to implement custom tags that make solving a particular problem easier than using normal HTML. Imagine having tags like;



    or just;

    These custom tags still render normal HTML to the browser but reduce the effort in developing the user interface and come with their own server side “behaviour”, allowing you to do things like “attach” the result of a database query to them, as you’ve seen above.

    or

    are doable (although not done yet).

    if you’re going to practice making tables, why not practice making them accessible as well?

    Good point – sounds like a feature request. Development of WACT generally follows the lines of what gets done is what someone needed at the time, rather than aiming for perfection first time.

  • http://limb-project.com pachanga

    Hi, very interesting challenge and i’d like to show how the same result can be achieved with LIMB. Please keep in mind that i’m not trying to say it’s better – it’s just a bit different and maybe a bit more complicated(?)(we’re using WACT too).

    The most interesting part is the template itself. The layout template remains the same so let’s move on to the table rendering template. There will be a detailed explanation below:





    URL
    {$Name}


    As you may have noted we use DATASOURCE to fill the resulting table component. It’s placed in the template, this way it makes LIMB templates more active. It’s way more like Delphi alike form components.

    Next is the pager, it’s different from the WACT one too.
    Firstly, it is active, the pager transparently controls the limit of the queried dataset, it also tries to catch the limit, offset attributes from the request and pass it to the datasource.
    The connection between the pager and datasource is established via ‘navigator’ attribute in the DATASOURCE tag. You can set the limit of the items per page by ‘items’ attribute in it.
    Secondly, we tried to make our pager more customizable for the web designer.
    Since it’s easy to customize the pager layout it’s declaration is a bit verbose and messy, however its contents is changed very seldom for whole site, so i put into separate template(pager_contents.html):

    total {$number}:

    << < [{$number}] [{$number}] [{$number_begin} - {$number_end}] ... > >>

    We don’t use WACT tag, instead we use the general tag. It has no clue about html, it just iterates over the result set passed from the DATASOURCE via ‘target’ attribute.

    The most interesting part is the php datasource(it’s defined via sub tag):


    require_once(LIMB_DIR . 'core/datasource/datasource.class.php');
    require_once(LIMB_DIR . 'core/lib/db/db_factory.class.php');

    class phpmodules_list_datasource extends datasource
    {
    function & get_dataset(&$counter, $params)
    {
    $db =& db_factory :: instance();

    $db->sql_select('SELECT * FROM phpmodules', $params['limit'], $params['offset']);
    $arr = $db->get_array();

    $db->sql_select('SELECT COUNT(*) as counter FROM phpmodules');
    $row = $db->fetch_row();
    $counter = $row['counter'];

    return new array_dataset($arr);
    }
    }

    As you can see, you’re always required to count the whole result set, this way the pager will work properly. Also you get all misc. query parameters via $params argument, the pager will dynamically pass required limit and offset.

    The main program is quite simple:


    < ?php
    require_once('setup.php');
    require_once(LIMB_DIR . '/core/template/template.class.php');

    $Page =& new Template('/example1.html');
    $Page->display();
    ?>

  • jbardin

    I have a poll for all of the readers Having no experience in any of the mentioned “ways”:

    indicate by username which is the easiest example to “grep” (understand, read …etc)

  • Sam

    “as the following three examples, with less lines of code”

    “fewer lines,” not “less lines.”

  • phunkphorce

    But how about more complex situations? “SELECT * FROM books” it’s a very easy one but we all know that this would be the most optimistic of the most optimistic real-world scenarios… How would one go about generating a table with all the books in our database, showing also how many users have bought each one of them and all the list of categories to which each one of the books belongs (and there can be more than one category per book)? It’s not a one-line query anymore, is it?

    Or how about a data abstraction layer where every row in the database table is mapped to an object in our object model? Could we still WACT’s approach for this, since every row from the database possibly needs some manipulation before it can be output?

  • http://blog.casey-sweat.us/ sweatje

    which is the easiest example to “grep” (understand, read …etc)

    I think the word you are looking for is “grok

  • http://blog.casey-sweat.us/ sweatje

    [quote=phunkphorce]Or how about a data abstraction layer where every row in the database table is mapped to an object in our object model? Could we still WACT’s approach for this, since every row from the database possibly needs some manipulation before it can be output?[/quote]

    The WACT data:table is iterating over a database result set, so this essentially could be any query: i.e.

    select b.name, b.author, sum(s.qty) as qty_sold
    from book b, sales s
    where b.id = s.id

    would work fine, and would automaticallhy produce a three column table with book name, author and quantity sold.

    As to the second question, if there is a greater need for manipulation of the result set, you can add a filter to the result set to perform additional row based calculations before presenting the output.

  • jbardin

    Indeed, grok is what I meant. As for ‘complex queries’ is doesnt quite matter what the query is because in the end its still a ‘matrix’ (and the challange was matrix2html, where matrix was a simple query of all the records in a table). And you can do ALOT in the SQL itself, especially if you are working with a DB that supports nested queries (and or triggers).

  • http://maetl.coretxt.net.nz/ maetl

    Unfortunately don’t have an Apache server that I can test it at the moment, but I’m certain that using http://www.rubyonrails.org, it would be easy to generate similar HTML direct from SQL with a miniscule amount of code compared to the various PHP solutions… Will have to examine this more closely…

  • Captain Proton

    Nice example, but does it also offer you the flexibility of changing your data source (to an XML file for example), sorting the listing by clicking on the column headers, or even completely changing the paging algorithm to something like a-page-per-category? I think not :)

  • http://blog.casey-sweat.us/ sweatje

    Captain Proton

    Nice example, but does it also offer you the flexibility of changing your data source (to an XML file for example), sorting the listing by clicking on the column headers, or even completely changing the paging algorithm to something like a-page-per-category? I think not :)

    A link please?

  • Rob…

    ADOdb has rs2html()

    Example from the manual:
    < ?
    include(‘tohtml.inc.php’); # load code common to ADOdb
    include(‘adodb.inc.php’); # load code common to ADOdb
    $conn = &ADONewConnection(‘mysql’); # create a connection
    $conn->PConnect(‘localhost’,'userid’,”,’agora’);# connect to MySQL, agora db
    $sql = ‘select CustomerName, CustomerID from customers’;
    $rs = $conn->Execute($sql);
    rs2html($rs,’border=2 cellpadding=3′,array(‘Customer Name’,'Customer ID’));
    ?>

    No idea about formatting it though!

  • Captain Proton

    A link please?

    It’s a localhost link, so I can’t give you any.

    What I’m trying to say, is that this data table feels a bit like it’s a all-done-and-ready solution which you just “pop into” your code and it works, but it was not designed to be customized a lot more than some colors.

    My solution works by providing a core class which hardly does anything, but it requires a number of objects implementing some interface to do the work of sorting and paging for example. The ‘View’ components, which actually display the list, are also completely separated from those that read in request parameters (page, sort by, sort order, etc) and map those to a sort command on an sql query (or any data source: using something like a IDataListQuery interface). By encapsulating all possible variants in the process of ‘displaying a data list’ under an interface, you can create any type of data list you want with any sort of behaviour.

    A Challenge :)

  • http://www.phppatterns.com HarryF

    Nice example, but does it also offer you the flexibility of changing your data source (to an XML file for example)

    It can be done, depending on the structure of the document. If you look at the basic data table example, it’s using PHP arrays (albeit ones that look like a database result set). You need to implement WACT’s Iterator and DataSpace interfaces with whatever provides access to the XML.

    sorting the listing by clicking on the column headers

    With the current form of this tag you’ve need to use the tag to provide a link on the column headings then handle the sorting based on GET variables from your PHP controller. Jeff’s been experimenting with an tag which “talks” to the front controller in this example and allows URL “re-writing” – something similar would be needed to allow the data table tag to be intelligent about providing a mechanism for server-side sorting (client side Javascript sorting would be fairly easy to do right now).

    or even completely changing the paging algorithm to something like a-page-per-category? I think not :)

    Also do-able to an extent. You’d need to decorate / reproduce the pager component’s API. In practice it would be easier to use WACT’s list tag to display the categories for “paging” through and handle each category with a different “case” in a controller.

    Generally what’s getting implemented is common use cases that someone has a need for right now.

  • Ronen Botzer

    These custom tags still render normal HTML to the browser but reduce the effort in developing the user interface

    I’ve done a lot of JSP work recently, and I agree that custom tags are a good idea if the programmer is doing all the work (i.e. layout design, layout integration and backend coding). However, I think it’s a bad idea when you have more resources and attempt a division of labor approach.

    Secifically, the layout designers I’ve worked with have no idea how to visualize the custom XML tags, and I end up chopping layouts into custom tags which is tiresome and must be repeated when the layout changes.

    Since WACT is supposed to be have a component approach wouldn’t it be better for a designer to create the template in straight HTML, along with appropriate id attributes for all the dynamic areas, and have WACT modify/iterate the results?