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

Hello Perl Monks,

I would like to be able to populate a Perl associative array with form values like this:

<input name="form_item{foo}" value="one"> <input name="form_item{bar}" value="two"> <input name="form_item{fiddle}" value="three">

I would like this to result in something like:

$hashref = $q->param('form_item');

And then be able to reference the values like this:

print $hashref->{foo}; # one print $hashref->{bar}; # two print $hashref->{fiddle}; # three

It seems that CGI.pm falls short of this, only allowing you to pass arrays, where the values are returned as a Perl list. Is there any way to get this type of functionality, short of rolling your own CGI package? (I've been warned not to do that!) :^)

Thanks,
-brian

Replies are listed 'Best First'.
Re: Populating a Perl associative array with form values
by davido (Cardinal) on Apr 29, 2004 at 00:00 UTC
    Not exactly how you're doing it, but returning a hash of keys and values is what the CGI module does, and is, in fact, one variant of its primary function.

    To get a reference to an anonymous hash containing all params and values, do this:

    my $q = CGI->new(); my $params = $q->Vars; print $params->{'foo'}; # one

    If you prefer getting a named hash instead of a hashref, do this:

    my $q = CGI->new(); my %params = $q->Vars; print $params{'foo'}; # one

    Update (additional info): This is documented in the POD for CGI:

    Many people want to fetch the entire parameter list as a hash in which the keys are the names of the CGI parameters, and the values are the parameters' values. The Vars() method does this. Called in a scalar context, it returns the parameter list as a tied hash reference. Changing a key changes the value of the parameter in the underlying CGI parameter list. Called in a list context, it returns the parameter list as an ordinary hash. This allows you to read the contents of the parameter list, but not to change it.

    When using this, the thing you must watch out for are multivalued CGI parameters. Because a hash cannot distinguish between scalar and list context, multivalued parameters will be returned as a packed string, separated by the "\0" (null) character. You must split this packed string in order to get at the individual values. This is the convention introduced long ago by Steve Brenner in his cgi-lib.pl module for Perl version 4.

    Update2 (afterthought): Just to be thorough, I wanted to show the technique that will give you a named hash with multi-values placed into anonymous arrays:

    my $q = CGI->new(); my %hash = $q->Vars(); foreach my $key ( keys %hash ) { my $values = $hash{$key}; $hash{$key} = [ split /\0/, $values ]; }

    Now you'll have a HoA (hash of arrays). This takes care of the potential for multi-value keys by placing all values into anonymous arrays referred to by the individual hash elements. It's now a 2d structure. Plan accordingly ;)

    HTH


    Dave

Re: Populating a Perl associative array with form values
by TilRMan (Friar) on Apr 29, 2004 at 01:19 UTC

    In short, no, CGI won't do that for you, because that's not really its job. You can do it yourself pretty easily though. I'm going to simplify your example a little by using a dot instead of curlies.

    <input name="form_item.foo" value="one"> <input name="form_item.bar" value="two"> <input name="form_item.fiddle" value="three">

    Now you can "split" on the dot to nest the hashes. The code below works recursively, so you can have multiple dots in a field name and they will be broken into several levels of hashes.

    sub nest_hash { my ($delim, $hash) = @_; my $new; while (my ($key, $value) = each %$hash) { unless ($key =~ /\Q$delim\E/s) { $new->{$key} = $value; next; } my ($before, $after) = ($`, $'); my $temp = nest_hash($delim, { $after => $value }); while (my ($k, $v) = each %$temp) { $new->{$before}{$k} = $v; } } return $new; } my $params = $q->param(); my $nested = nest_hash('.', $params); my $hashref = $nested->{form_item}; print $nested->{form_item}{foo}; # one print $hashref->{fiddle}; # three

    Code is only partially tested.

      Thank you, this is exactly what I am trying to do. I will use some adaptation of this code.

      -brian

Re: Populating a Perl associative array with form values
by ccn (Vicar) on Apr 29, 2004 at 00:03 UTC
    my %hash = map {/\(([^\)]+)\)/; $1 => $cgi->param($_)} grep /^form_ite +m/, $cgi->param;