http://qs321.pair.com?node_id=611002

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

What I would like to do is use DFV to validate that at least one of a set of HTML form entries (call them foo_1, foo_2, etc.) has been chosen. This may sound like an XY problem, but since I'm already using DFV for other aspects of validation (checking email, checking passwords), this seems like a reasonable thing to attempt.

DFV has profile entries for "require_some" (require N entries from a set of fields) as well as "required_regexp" (specify required fields with a pattern--this is close, but it ends up making all of foo_* required, which is bad). Apparently what I want is "require_some_regexp", where I can specify that I want e.g. one or more of qr/^foo_\d+$/.

I cobbled this constraint together:

sub require_some_regexp { my ($n, $re) = @_; return sub { my $dfv = shift; $dfv->name_this('require_some_regexp'); my $count = grep /$re/, keys %{ $dfv->get_filtered_data }; return $count >= $n; }; }
... but then I realized that in the "constraint_methods" portion of the profile, the keys are the (hardcoded) form fields to be constrained, so that's not going to work. I could tie it to some hidden field that's guaranteed to be submitted, but that's just a workaround.

There's also "constraint_method_regexp_map", but again I don't know ahead of time what the fields will be. I could use regexp qr/./, but then what happens if 99 foo_* fields have values? My validation code (which needs to be run only once) will be run 99 times, which is suboptimal.

I'm starting to think that DFV just can't do it. I've even looked at the source, thinking I could plug in a method or three, but the "check" method is just a thin wrapper to the DFV::Results object constructor, which is 300 (dense) lines long, I kid you not. So I'm not too keen on doing anything in there.

TIA for all the help I know I'm going to get here.

Replies are listed 'Best First'.
Re: Data::FormValidator, equivalent to "require_some_regexp"?
by Herkum (Parson) on Apr 20, 2007 at 12:37 UTC

    I think the mistake you are making is assuming that the DFV profile has to be hard-coded or that the DFV profile has to do all the work.

    Dynamically generate a DFV profile. It is only a hash structure so there is no reason that this will not work.

    use CGI; my $q = CGI->new(); my @fields = $q->param(); my @foo_fields; for my $field (@fields) { push @foo_fields, $field if $field qr{^foo_}xm; } my $profile = {}; $profile->{require_some} => { foo_fields => [1, @foo_fields ] };
      I think that code can be tightened up a bit:
      $profile->{require_some} = { foo_fields => [ 1, grep /^foo_\d+$/, $q->param ] };
      But apart from that I'm afraid this is the best solution available to me. Thanks!