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

Here is a short description of the issue:
I am to make a cgi script that accepts 'orders'. These orders consist of a record that describes the general order information (ship_to's, bill_to's, etc...) and a varaible number or 'lines' that describe the actual pieces that make up the order.

OK, so far so good with the general order information. The problem I have is deciding the best way to handle the variable number of lines. Below is a list of my possible solutions, please advise which is best (experienced opinions are my favorite) or give solutions that I myself have not thought of.

It is also planned that in the future, the definition of which fields are required for an order and for each line can be loaded from a DB so that each 'client' can have their own checks and validation for their complete orders.

I can see pros/cons to each approach, but I have tried to leave most of my opinion out. I am here to learn from others, not to stand on a soap box. :-)

the_Don
...making offers others can't rufuse.

Replies are listed 'Best First'.
Re: Handling Dynamic URL Parameters
by Solo (Deacon) on Sep 13, 2002 at 17:10 UTC
    Append line numbers to the fields so I can bypass hash and array manipulation entirely. i.e item_no_1=1&item_no_2=2&item_no_3=3&qty_1=11&qty_2=12&qty_3=13)
    I make heavy use of this approach with sometimes more complex 'keys'. For instance:
    use strict; use warnings; use CGI; my $q = CGI::new; # or however you get your $q ... # input names look like "id1~id2~id3" # or pick a delimiter you like instead of ~ for my $param ($q->param) { # print "$param<br/>\n"; next unless $param =~ /.*(?:~.+){2}/; my ($id1,$id2,$id3) = split(/~/,$param); # print "$id1 - $id2 - $id3<br/>\n"; # ... }
    (Edit: minor code addition, use strict, etc. :)
    --
    May the Source be with you.

    You said you wanted to be around when I made a mistake; well, this could be it, sweetheart.

      I'm not familiar with how CGI returns values, and I dont understand how this script works exactly.

      for my $param ($q->param)
      This gets an array of all the parameter names

      next unless $param =~ /.*(?:~.+){2}/;
      This checks for zero or more of any character followed by 2 occurances of either ?:~.+

      my ($id1,$id2,$id3) = split/~/,$param);
      $param is a string of values and you spilt on the ~

      I guess my confusion is on the distinction between parameter name and values here. Could you give an example URL that you would pass to have this script work?

      the_Don
      ...making offers others can't rufuse.

        This checks for zero or more of any character followed by 2 occurances of either ?:~.+
        The (?: ... ) construction is 'pure grouping.' So (?:~.+){2} matches, but does not assign to a variable, a tilde ~ followed by one or more chars twice.... basically checking for 2 tildes. Somebody else could write this more elegantly, I'm sure.

        Here's an example querystring. I'll use the names that shotgunefx suggested.

        ?item~1~name=boobtube&item~1~qty=1&item~1~code=mysqlpk&item~2~name=loo +sifer&item~2~qty=1&item~2~code=666&ship~firstname=Han ...
        Since now we're dealing with fields with 2 and 3 ids, we need to adjust the code some.
        # Always use warnings; use strict; use CGI; my $q = CGI::new; my %lineitem; my %ship; # process params # for my $param ( $q->param ) { my @id = split(/~/,$param); # a SWITCH would work nicely, but I'll try # not to confuse the example if ( $id[0] eq 'item' ) { $lineitem{$id[1]}{$id[2]} = $q->param($param); } if ( $id[0] eq 'ship' ) { $ship{$id[1]} = $q->param($param); } } # process lineitems # print "Please confirm order of<br/> "; for my $item ( keys %lineitem ) { print $lineitem{$item}{qty}, $lineitem{$item}{name}; print "<br/>"; } print "<br/>being shipped to $ship{firstname}<br/>";
        Is this a better example?

        (Edit: minor spelling and grammer fixes)
        (Edit: replaced my ($id1,$id2,$id3) = with my @id =)

Re: Handling Dynamic URL Parameters
by LTjake (Prior) on Sep 13, 2002 at 18:27 UTC
    I like Zaxo's suggestion.

    do something like (NB: untested code!):
    # using Vars... use CGI; $q = new CGI; $params = $q->Vars; foreach $key (keys $params) { # If the param is just a number then it is # assumed to be an item number. You may want to # change that assumption. if ($key =~ /^\d+$/) { # Do something with $params->{$key} } }
    --
    # using param... use CGI; $q = new CGI; # If the param is just a number then it is # assumed to be an item number. You may want to # change that assumption for (grep(/^\d+$/, $q->param)) { # Do something with $q->param($_) }
    (Thanks to Zaxo for code help)
Re: Handling Dynamic URL Parameters
by shotgunefx (Parson) on Sep 13, 2002 at 16:28 UTC
    I'd go with something like this
    item-1-name item-1-qty item-1-code ship-firstname ship-lastname etc...


    -Lee

    "To be civilized is to deny one's nature."
Re: Handling Dynamic URL Parameters
by Zaxo (Archbishop) on Sep 13, 2002 at 16:02 UTC

    Another possibility is to make the catalog number the field name, and the quantity its value.

    After Compline,
    Zaxo

      make the catalog number the field name, and the quantity its value

      How would I get the parameter name?

      And my examples are not sufficient because there will be more than just two fields defining a line. Right now there are five, but that may increase.

      the_Don
      ...making offers others can't rufuse.

        How would I get the parameter name?

        if you're using CGI.pm, you can get the parameter list by calling the param method without arguments:
        my $q = new CGI; my @params = $q->param();

        FWIW, I'd go with the  num_of_lines=5&item_num_1=1234&item_num_2=4562... approach.

        -- Dan