I often find myself generating HTML tables from SQL tables, with some amount of formatting and munging in between. A handy technique that I use often is to put as much of the column description into one place as I can, then have a general loop that I can apply to each row of data. This lets me tweak many settings in one place and avoids lots of redundancy (in both perl and HTML).
# since the order of the columns is significant, we # use an array to hold information about each column. my @COL_INFO = ( { name => 'foo_id', hide => 1 }, { name => 'foo', id => 'foo_id', width => 400, gen => \&gen_foo, title => 'Foo' }, { name => 'bar', width => 200, align => 'right', title => 'Barque' } );
If I need other utility arrays or hashes, I can generate them all from the one central store above:
# now we can derive whichever other hashes or arrays # we might want from the above data. having a list # of column names is always handy: my @COL_NAMES = map $_->{name}, @COL_INFO; # and we might want to look up columns by name: my %COL_BY_NAME; @COL_BY_NAME{ @COL_NAMES } = @COL_INFO; # and to find the index of a column name: my %COL_INDEX; @COL_INDEX{ @COL_NAMES } = 0 .. $#COL_INFO;
This degree of uniformity lets us use very regular loops to print out headers and rows, and do all our checking and formatting in one place:
# ------------------------------------------------------- # the information is centralized for other uses, such as # printing headers: print "<tr>"; foreach my $info ( @COL_INFO ) { # skip hidden fields next if $info->{hide}; # build up opening tag my $header = "<th"; if ( my $width = $info->{width} ) { $header .= qq| width="$width"|; } $header .= ">"; print( $header . escapeHTML( $info->{title} ) . "</th>\n" ); } print "</tr>\n"; # ------------------------------------------------------- # then for each row: while ( my $cur = $sth->fetch() ) { # if necessary, you can construct a hash using # whatever keys you want, not just db column names: my %row; @row{ @COL_NAMES } = @$cur; # or maybe just pick out one value my $foo_id = $cur->[ $COL_INDEX{foo_id} ]; # formatting the output is now easier, too: print qq|<tr valign="top">\n|; foreach my $info ( @COL_INFO ) { my $cell = "<td"; $cell .= qq| align="$info->{align}"| if $info->{align}; $cell .= ">"; if ( my $gen = $info->{gen} ) { $cell .= $gen->( $cur ); } else { my $ix = $COL_INDEX{ $info->{name} }; $cell .= escapeHTML( $cur->[$ix] ); } $cell .= "</td>\n"; print $cell; } print "</tr>\n"; }
I like this centralized mechanism because I only have to change one place to add or remove a column, and I only have to code tricky things (remembering to HTML-entify strings, etc) in one place. Also, the description itself could eventually be extracted from a database, leading to even more dynamic (while still "typesafe") and well-formatted output.
Edit by BazB: added readmore tags.
In reply to One structure to describe multiple arrays or hashes by tkil
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |