Creative use of Data:Table

Harry recently blogged about the Data:Table component I wrote for WACT. His post talked about the common method of using the data:table tag, i.e. feed it a data set and have it generate a table, perhaps using some declarative markup in the template to modify the results. Used in this context, you can output nicely formatted HTML tables with minimal amounts of code.

I had an interesting problem recently that challenged the way I used the data:table component. I needed to display 18 months of data in a table. Each row represented a different item, and the months were to be laid out in the columns of the table.

When I started to work with the table, I initially started with the traditional style of putting all of the tags specifying the layout in the template. With just two months of data, it might have looked something like this:

<data:table from="tableData" autogen="n">
  <data:column name="itemdesc">
    <data:header>Description</data:header>
  </data:column>
  <data:group>
    <data:header>{$m1label}</data:header>
    <data:column name="m1val">
      <data:header>Value</data:header>
      <data:cell>{$m1val|number}</data:cell>
    </data:column>
    <data:column name="m1val2">
      <data:header>Input</data:header>
      <data:cell><input type="text" name="{$m1key}" value="{$m1val2|number}"></data:cell>
    </data:column>
  </data:group>
  <data:group>
    <data:header>{$m2label}</data:header>
    <data:column name="m2val">
      <data:header>Value</data:header>
      <data:cell>{$m2val|number}</data:cell>
    </data:column>
    <data:column name="m2val2">
      <data:header>Input</data:header>
      <data:cell><input type="text" name="{$m2key}" value="{$m2val2|number}"></data:cell>
    </data:column>
  </data:group>
</data:table>

You can see where the group of columns for month two is just a cut and paste with minor tweaks to the variables. While this worked fine, I knew this was not what I wanted to maintain for the long run. Ideally I could use a loop to specify the templates for a single month once and have it increment through the values, but the data:table component does not support other WACT looping structures inside of it (a list component), and WACT templates do not support variable variables that would be required to iterate over the months.

This was where my creativity came into play. I decided to continue to use the data:table component, but pull some of the automatically generated code back into my php script. What I did to start was to look at what the compiled template output was, and then to place much of the runtime component manipulation into my script that was running the WACT template code. I was able to clean up and improve the automatically generated code alone the way (which is a good thing, glad I can code better than a code generator 😉 ). The resulting template and code looked something like this:

Template:

<data:table from="tableData" autogen="n">
</data>

Code:

<?php

$c =& new DataColumnComponent;
$c->setHeaderFunct(create_function('', 'echo "Description";'));
$c->outputTh();
$c->setRenderFunct(create_function('&$tds, &$root'
    ,'echo htmlspecialchars($tds->get("itemdesc"));'));
$Table->registerColumn('desc',$c);

foreach (range(1,18) as $iCol) {
  $g =& new DataGroupComponent;
  $g->setHeaderFunct(create_function('&$tds, &$root'
    ,'echo htmlspecialchars($tds->get("m'.$iCol.'label"), ENT_QUOTES);'));
  $filter =& new timeFenceCellFilter($iCol);

  $c1 =& new DataColumnComponent;
  $c1->parent =& $Table;
  $c1->registerCssClassFilter($filter);
  $c1->setRenderFunct(create_function('&$tds, &$root'
    ,'echo htmlspecialchars(number_format($tds->get("m'.$iCol.'val")), ENT_QUOTES);'));
  $c1->setHeaderFunct(create_function('',' echo "Value";'));
  $c1->registerGroup($g);

  $c2 =& new DataColumnComponent;
  $c2->parent =& $Table;
  $c2->registerCssClassFilter($filter);
  $c2->setRenderFunct(create_function('&$tds, &$root'
    ,'echo '<input type="text" name="', $tds->get("m'.$iCol.'key"), '" value="',
    htmlspecialchars(number_format($tds->get("m'.$iCol.'val2 ")), ENT_QUOTES), '" />';'));
  $c2->setHeaderFunct(create_function('',' echo "Input";'));
  $c2->registerGroup($g);

  $Table->registerColumn('m'.$iCol.'val', $c1);
  $Table->registerColumn('m'.$iCol.'val2 ', $c2);
  $Table->registerGroup($g);
}
</input>
?>

Sorry for the formatting, it appears the syntax highlighter I am using for wordpress gets a little confused around all the quoting I am doing in this code.

So basically I ended up with exactly what I needed for this scenario: the code for all 18 months of grouped columns was generated in a loop, and therefore the “template” for the columns only has to be edited in one location. The downside is that I am no longer using the nice neat template syntax, but instead I am writing create_function() calls to output the desired cell contents, just like what the compile time component for the data:table does when creating the compiled template.

Anyway, thought I would give you a sneak peak into a creative usage of the WACT data:table component.