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

Monastic Monks,

Imagine you have a user sign-up form in html. Name, address, etc.

But now you want to have a single form with input fields for 50 users at a time. 50 name fields , 50 address fields, etc. You'll be dynamically generating the form from a Perl script and TK.

You'll be processing the form with cgi.pm to store it all in a db.

What's a good strategy for setting up the form names that cgi.pm can most effectively exploit?

For example, you could name all the Name fields "name_x", where x is indexed from 1 to 50, and then for processing do something like:

$query = new CGI; @form_field_names = $query->param; foreach $field_num_pair (@form_field_names) { ($field, $index) = split(/_/, $field_num_pair); $form_values{$index}{$field} = $query->param($field_num_pair); }

But I can't help thinking there must be something more elegant that can be done in the structure of the html form to distinguish the users and that can be used more easily by cgi.pm. For example, using both name and ID tags on form elements if cgi.pm picks them up seprately (Except I don't think all browsers send the ID)

Thanks.




Forget that fear of gravity,
Get a little savagery in your life.

Replies are listed 'Best First'.
Re: Strategy to name/process many similar form fields with cgi.pm?
by Rhandom (Curate) on Apr 06, 2007 at 00:04 UTC
    Until recently I have used the strategy that you have shown above. I would now suggest using Data::URIEncode.

    You would begin by using a form that looks similar to the following:

    <form ...> <table> <tr> <td><b>Username 1</b></td> <td><input type=text name="users:0.username"></td> </tr> <tr> <td><b>Email</b></td> <td><input type=text name="users:0.email"></td> </tr> <tr> <td><b>Name</b></td> <td><input type=text name="users:0.name"></td> </tr> </table> <table> <tr> <td><b>Username 1</b></td> <td><input type=text name="users:1.username"></td> </tr> <tr> <td><b>Email</b></td> <td><input type=text name="users:1.email"></td> </tr> <tr> <td><b>Name</b></td> <td><input type=text name="users:1.name"></td> </tr> </table> </form>


    So far so easy. You can use pretty much whatever templating tools you want for generating the html - clear from hard coded html (don't do that), to CGI html methods, to Template::Toolkit (I'd suggest doing that).

    When the form is posted to cgi script, just pass the CGI query through Data::URIEncode.

    use CGI; use Data::URIEncode qw(query_to_complex); use Data::Dumper qw(Dumper); my $q = CGI->new; my $data = query_to_complex($q); print "Content-type: text/plain\n\n"; print Dumper $data; __END__ Will have printed something similar to: $VAR1 = { users => [{ username => "...", email => "...", name => "...", },{ username => "...", email => "...", name => "...", }], };


    You'll still need to do all of the validation, but getting the records into the right structure is already done.

    my @a=qw(random brilliant braindead); print $a[rand(@a)];
      Now THAT's the kind of thing I suspected was out there, if someone would just ask. Very nice. Thanks.




      Forget that fear of gravity,
      Get a little savagery in your life.
Re: Strategy to name/process many similar form fields with cgi.pm?
by philcrow (Priest) on Apr 05, 2007 at 20:20 UTC
    The browser will only give you names and values. Your numbering approach is about the best possible. I'm having trouble imagining something simpler than your sample.

    Phil

Re: Strategy to name/process many similar form fields with cgi.pm?
by ruoso (Curate) on Apr 06, 2007 at 11:06 UTC

    Even knowing that this wasn't your question, I'd like to point out something that may help you even more, considering this will run in a controlled environment.

    XForms is a not-so-new-but-not-so-known technology that implements a client-side MVC infrastructure to HTML forms. This is a W3C standard (http://www.w3.org/TR/xforms/) that has a quite stable support in mozilla by the use of an extension (http://www.mozilla.org/projects/xforms/), and recently CGI.pm has accepted a patch I made to support XForms requests.

    When you install the XForms extension, you can see the mozilla examples (http://www.mozilla.org/projects/xforms/samples.html), and you'll see how this can help in controlled environments such as intranets. The difference is that you'll receive structured data as XML, instead of having to workaround it in html.

    There's also a patch for HTTP::Body (http://rt.cpan.org/Ticket/Display.html?id=22057) to support that, but that patch wasn't accepted yet.

    daniel
Re: Strategy to name/process many similar form fields with cgi.pm?
by Anonymous Monk on Apr 06, 2007 at 20:17 UTC
    CGI.pm includes import_names, a function (method?) that imports all input into a namespace. $q->import_names('INPUT'); will put import all input variables into the namespace INPUT. Thus, your "name" form field will be in $INPUT::name.

    Conveniently, if the input variable is multi-valued (multiple fields named "name"), import_names creates an array that can be accessed as @INPUT::name.

    So your problem can be solved with:

    use CGI; $q = new CGI; $q->import_names('INPUT'); for(my $i=0; $i<=$#INPUT::name; $i++) { ### process each related record ### presuming each record has name, address, etc, fields ($FORM{$i}{name},$FORM{$i}{address},$FORM{$i}{etc}) = $INPUT::name[$ +i],$INPUT::address[$i],$INPUT::etc[$i]) if $INPUT::name[$i]; # if no +name, the hash isn't populated. }

    This only becomes a "problem" if you have checkboxes used as input fields for each record. An unchecked checkbox is not sent with the form.
Re: Strategy to name/process many similar form fields with cgi.pm?
by Brovnik (Hermit) on Apr 11, 2007 at 11:33 UTC
    "50 users at a time. 50 name fields , 50 address fields,"

    That rings alarm bells for me.

    Either :
    1. You are asking someone to manually input details of 50 users at once, or

    2. You are expecting an automaton to fill in the form.

    In the First case, presumably the users has the input in some other form and is retyping it. If so, get access to the source data (e.g. a spreadsheet or other data file) and input that directly. You will save the user a lot of typing, and will reduce typos and errors dramatically.

    In the second case, use a more lightweight mechanism like Soap::Lite to create a server process to receive new user registrations, in which case they can be done one at a time.

    Any time you are looking to do something 50 times on a web page, there has to be a better way.

      use a more lightweight mechanism like Soap::Lite

      SOAP::Lite is not really lightweight. Personally, I'd just POST the data from within a loop.