Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, hope someone can help out here with my 1st question to the monks.

I'm just starting to venture into the world of CGI.pm and trying to use the module instead of hand coding HTML.

My main problem is something which I suspect is simple, and just down to my inexperience - I'm reading some data from mysql and generating a table to display the output. I've no problem getting the data from mysql, but what I'm having problems with is displaying it using pure CGI inside a loop.

At the minute, I'm mixing hand coded HTML & CGI object's as shown below:

my $html_page = new CGI;
...
print "<TABLE>\n"; while (my $db_data = $sth->fetchrow_arrayref()) { print $html_page->Tr( $html_page->td($db_data) ); } print "</TABLE>\n";
I suspect there must be a way of integrating the loop into the print, something like
print $html_page->table( while ...... { $html_page->Tr( $html_page->td($db_data) ) } );
but at the minute I'm lost

Replies are listed 'Best First'.
Re: CGI Tables
by Zaxo (Archbishop) on Sep 01, 2002 at 03:18 UTC

    You're very close, and I like that you're using the implied map that CGI.pm invokes on array references. Since you're putting all your results on one page of html, I'll assume that there is no problem getting the whole DBI query at once.

    print $html_page->table( $html_page->Tr( [ map {$html_page->td($_)} @{$sth->fetchall_arrayref()} ] ) )

    The fetchall_arrayref function returns a reference to an AoA of all the results of the db query. Mapping the td() function over the dereferenced result gives an array of rows. Then we apply Tr to a reference to that array ( the square brackets) to get html row tags applied to each.

    After Compline,
    Zaxo

      Excellent, I had a feeling that it would be simple. As you can probably guess I've got to get my head round map - obviously very useful, so I'll just have to persevere. Thanks for your help, sch

      OK, to expand on this a little - and I hope you can bear with me on this as I'm trying to get my head round this still.

      I now want to expand on this to include items not returned by the fetch from the database, e.g. to include a record count as the first item on each line of the table

      I've worked out (after some head scratching) that I can use the following:

      print $html_page->table( $html_page->Tr( [ map {$html_page->td($record_count++).$html_page->td($_)} @{$ +sth->fetchall_arrayref()} ] ) );

      which works fine - but I was wondering if there is a more efficient way of doing this?

        That looks fine. So long as the tables are small enough to avoid memory pressure, that is probably as efficient as you need it to be.

        The construction is done without waiting for I/O, all in memory. The memory used to collect the string for printing is immediately released.

        What sort of efficiency do you need? If you're not sure, try profiling and benchmarking to see what is costing you. Don't optimize ahead of the facts.

        After Compline,
        Zaxo

Re: CGI Tables
by tadman (Prior) on Sep 01, 2002 at 03:11 UTC
    You can't quite do it that way. Rember that each CGI HTML function returns a bit of text that has to be used. Inside that while loop, you're just discarding the results. They're not being fed back into the main table() call. What you need is something like this:
    print $html_page->table( $html_page->tr( map { $html_page->td($_) } .... ) );
    This will probably have to be re-tooled for your application. Remember that a while call does not return any data, yet a map call does. Update: I think Zaxo has a more thorough example.

    As a note, I would shorten your $html_page variable. Single-letter variable names are fine, provided it's clear what they are. I frequently use $q for a CGI query, $r for an Apache request, and so forth. It gets kind of difficult to type out seven letters instead of two. Laziness, I know.

      Uh! Bad advice (the "shorten your variable names", not the map example)! Sorry, just my humble opinion.

      As soon as the scope of the variable you use is larger than 5 lines, use Identifiers::Long! ;)

      Then, if it's just 5 lines, you'll survive typing a long name two or three times either. When someone else will need to read your code -- and after 6 Months you yourself are someone else -- he'll be glad to have meaningful names.

      I banned $i for counters right after school (where the formula's my programs were based on used i, of course), and I never missed it. Even if it's an iteration I'd rather use $iteration_count. And in case of indices it's nice to expess what your're indexing.

      And let's be honest, typing $request especially in a coding situation takes me no longer that typing $r. Well almost... It surely takes more time to read (and lookup/translate and undestand) $r for anyone not knowing the obvious...

      If I don't want to repeatedly type an identifier with a fancy name I simply cut and paste. Of course, it's good to know how to do that using your keyboard... ;)

      There's enough abbreviation in Perl anyway ($_, $a, $b, $#array, etc.).

      So, while I'm with you that obfuscuated perl is cool and fun, yet for serious work it's cool to use @long_names, and lame to be $lzy.

      So long,
      Flexx

      PS: I know I am a style phanatic.

      Update: Humm... I just realized I talked back to a saint... :] (...) a lazy saint. ;)

        Think of variable names in a LZW compression style. The more you use it, the shorter it can get, but to a point. I was just saying that for things you use capital-A All The Time, you can get by with a single letter. For me, $r is Always the same thing. It's sacred.

        If you want, you can call it $apr, or whatever, but things like $apache_request are kind of like naming your dog The Happy Canine Who Is My Life-Long Friend Named Spot. When you're talking about $dbh or $cgi, you should have an idea what you're dealing with.

        What I don't like is functions like this:
        sub mulch { my ($x, $z, $zz, $a) = @_; $x->foo($z, $zz, $a->id()); $z->insert($x); $a->restore(); return $x; }
        Isn't that informative? Without backtracing through caller after caller, you have no idea what type the variables are. Sure, you could put in some diagnostic code, but isn't that kind of absurd?

        All I'm saying is that if you use someting A Lot, you can reduce the letter count accordingly. If you use it almost never, you should be pretty clear as to what it is.
Re: CGI Tables
by rinceWind (Monsignor) on Sep 01, 2002 at 09:50 UTC
    Although the OO interface to CGI.pm is useful for forms (especially if there are multiple forms on a page with diferent data), I tend to prefer using the exporter interface to CGI.pm.
    use CGI qw(:standard *table); ... print start_table; while (my $db_data = $sth->fetchrow_arrayref()) { print Tr(td($db_data)); # other processing in the loop for each element } print end_table;
    hth

    --rW