File Coverage

lib/Mojolicious/Plugin/Vparam.pm
Criterion Covered Total %
statement 234 245 95.5
branch 182 210 86.6
condition 109 149 73.1
subroutine 14 14 100.0
pod 1 1 100.0
total 540 619 87.2


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Vparam;
2 73     73   24420078 use Mojo::Base 'Mojolicious::Plugin';
  73         12384  
  73         475  
3 73     73   69965 use Mojolicious::Plugin::Vparam::Common qw(:all);
  73         170  
  73         9864  
4              
5 73     73   25042 use version;
  73         105019  
  73         383  
6 73     73   35708 use List::MoreUtils qw(firstval natatime mesh);
  73         481415  
  73         686  
7              
8             our $VERSION = '3.00';
9              
10             # Regext for shortcut parser
11             our $SHORTCUT_REGEXP = qr{
12             ^
13             (
14             [!?@~%] # symbols shortcut
15             |
16             (?:array|maybe|optional|required?|skipundef)\[ # text[] shortcut start
17             )
18             (.*?) # value
19             \]? # text[] shortcut end
20             $
21             }xi;
22              
23             sub register {
24 74     74 1 716721 my ($self, $app, $conf) = @_;
25              
26 74   50     309 $conf ||= {};
27              
28 74   50     610 $conf->{class} ||= 'field-with-error';
29 74   50     441 $conf->{types} ||= {};
30 74   50     433 $conf->{filters} ||= {};
31              
32 74   50     549 $conf->{vsort_page} ||= 'page';
33 74   50     448 $conf->{vsort_rws} ||= 'rws';
34 74   50     427 $conf->{rows} ||= 25;
35 74   50     462 $conf->{vsort_oby} ||= 'oby';
36 74   50     412 $conf->{vsort_ods} ||= 'ods';
37 74   50     407 $conf->{ods} ||= 'ASC';
38              
39 74   100     393 $conf->{phone_country} //= '';
40 74   100     381 $conf->{phone_region} //= '';
41 74   100     384 $conf->{phone_fix} //= '';
42              
43 74 50       318 $conf->{date} = '%F' unless exists $conf->{date};
44 74 50       306 $conf->{time} = '%T' unless exists $conf->{time};
45 74 100       292 $conf->{datetime} = '%F %T %z' unless exists $conf->{datetime};
46              
47 74   50     463 $conf->{blessed} //= 1;
48 74   50     366 $conf->{optional} //= 0;
49 74   50     421 $conf->{skipundef} //= 0;
50 74   50     366 $conf->{multiline} //= 0;
51              
52 74   100     450 $conf->{address_secret} //= '';
53              
54 74   50     346 $conf->{password_min} //= 8;
55              
56 74   50     393 $conf->{hash_delimiter} //= '=>';
57              
58             # Enable Mojolicious::Validator::Validation integration if available
59             $conf->{mojo_validator} //=
60 74 50 33     1885 version->new($Mojolicious::VERSION) < version->new(4.42) ? 0 : 1;
61              
62             # Get or set type
63             $app->helper(vtype => sub {
64 2000     2000   185428 my ($self, $name, %opts) = @_;
65 2000 100       9373 return $conf->{types}{$name} = \%opts if @_ > 2;
66 1         7 return $conf->{types}{$name};
67 74         652 });
68              
69             # Get or set filter
70             $app->helper(vfilter => sub {
71 1112     1112   92238 my ($self, $name, $sub) = @_;
72 1112 100       4236 return $conf->{filters}{$name} = $sub if @_ > 2;
73 1         6 return $conf->{filters}{$name};
74 74         1690 });
75              
76             # Get or set config parameters
77             $app->helper(vconf => sub {
78 7     7   38136 my ($self, $name, $value) = @_;
79 7 100       83 return $conf->{$name} = $value if @_ > 2;
80 1         10 return $conf->{$name};
81 74         1349 });
82              
83             # Get or set error for parameter $name
84             $app->helper(verror => sub{
85 537     537   44079 my ($self, $name, @opts) = @_;
86              
87 537   100     1535 my $errors = $self->stash->{'vparam-verrors'} //= {};
88              
89 537 100       6046 if( @_ <= 2 ) {
    100          
90 363 100       1737 return 0 unless exists $errors->{$name};
91              
92             return 'ARRAY' eq ref $errors->{$name}
93 5         28 ? scalar @{$errors->{$name}}
94 126 100 50     1002 : $errors->{$name}{message} // 0
95             ;
96             } elsif( @_ == 3 ) {
97 5 50       13 return 0 unless exists $errors->{$name};
98              
99             my $error = 'ARRAY' eq ref $errors->{$name}
100 8         43 ? firstval {$_->{index} == $opts[0]} @{$errors->{$name}}
  5         18  
101 5 50       26 : $errors->{$name}
102             ;
103 5   100     49 return $error->{message} // 0;
104             } else {
105              
106 169         274 my %attr = %{{@opts}};
  169         1881  
107 169 100       790 if( $attr{array} ) {
108 13 100       350 $errors->{ $name } = [] unless exists $errors->{ $name };
109 13         65 push @{$errors->{ $name }}, \%attr;
  13         33  
110             } else {
111 156         350 $errors->{ $name } = \%attr;
112             }
113              
114 169 50       522 if( $conf->{mojo_validator} ) {
115 169         528 $self->validation->error($name => [$attr{message}]);
116             }
117              
118 169         23447 return $errors;
119             }
120 74         1325 });
121              
122             # Return string if parameter have error, else empty string.
123             $app->helper(vclass => sub{
124 6     6   1357 my ($self, $name, @classes) = @_;
125 6 100       26 return '' unless $self->verror( $name );
126              
127 3         6 my @class;
128             push @class, $conf->{class}
129 3 50 33     30 if defined($conf->{class}) && length($conf->{class});
130 3         6 push @class, @classes;
131              
132 3         23 return join ' ', @class;
133 74         1254 });
134              
135             $app->helper(vvalue => sub{
136 6     6   11467 my ($self, $name, $default) = @_;
137              
138 6         17 my @input = params($self, $name);
139              
140 6         809 my $value;
141 6 100       22 if( not @input ) {
    100          
142 3         6 $value = $default;
143             } elsif( @input > 1 ) {
144 1         3 $value = \@input;
145             } else {
146 2         4 $value = $input[0];
147             }
148              
149 6         36 return $value;
150 74         1697 });
151              
152             # Return all errors as Hash or errors count in scalar context.
153             $app->helper(verrors => sub{
154 20     20   5348 my ($self) = @_;
155 20   100     71 my $errors = $self->stash->{'vparam-verrors'} //= {};
156 20 100       343 return wantarray ? %$errors : scalar keys %$errors;
157 74         1316 });
158              
159             # Many parameters
160             $app->helper(vparams => sub{
161 460     460   220645 my ($self, %params) = @_;
162              
163             # Result
164 460         735 my %result;
165              
166             # Get default optional
167             my $optional = exists $params{-optional}
168             ? delete $params{-optional}
169             : $conf->{optional}
170 460 100       1683 ;
171             my $skipundef = exists $params{-skipundef}
172             ? delete $params{-skipundef}
173             : $conf->{skipundef}
174 460 50       1306 ;
175             my $multiline = exists $params{-multiline}
176             ? delete $params{-multiline}
177             : $conf->{multiline}
178 460 50       1143 ;
179             my $blessed = exists $params{-blessed}
180             ? delete $params{-blessed}
181             : $conf->{blessed}
182 460 50       1118 ;
183              
184             # Internal variables
185 460   100     1275 my $vars = $self->stash->{'vparam-vars'} //= {};
186              
187 460         5363 for my $name (keys %params) {
188             # Param definition
189 533         5118 my $def = $params{$name};
190              
191             # Get attibutes
192 533         752 my %attr;
193 533 100       2210 if( 'HASH' eq ref $def ) {
    100          
    100          
    100          
    50          
194 223         810 %attr = %$def;
195             } elsif( 'Regexp' eq ref $def ) {
196 3         8 $attr{regexp} = $def;
197             } elsif( 'CODE' eq ref $def ) {
198 3         7 $attr{post} = $def;
199             } elsif( 'ARRAY' eq ref $def ) {
200 3         7 $attr{in} = $def;
201             } elsif( !ref $def ) {
202 301         681 $attr{type} = $def;
203             }
204              
205             # Skip
206 533 100       1372 if( exists $attr{skip} ) {
207 5 100       20 if( 'CODE' eq ref $attr{skip} ) {
    50          
208             # Skip by sub result
209 1 50       5 next if $attr{skip}->($self, $name);
210             } elsif( $attr{skip} ) {
211             # Skip by flag
212 4         10 next;
213             }
214             }
215              
216             # Set defaults
217 528   100     2556 $attr{optional} //= $optional;
218 528   66     2060 $attr{skipundef} //= $skipundef;
219 528   66     1968 $attr{multiline} //= $multiline;
220 528   66     2098 $attr{blessed} //= $blessed;
221              
222             # Apply type
223 528   100     1404 my $type = $attr{type} // $attr{isa};
224 528 100       1242 if( defined $type ) {
225             # Parse shortcut
226 515         4022 while( my ($mod, $inner) = $type =~ $SHORTCUT_REGEXP ) {
227 50 50       137 last unless $inner;
228 50         96 $type = $inner;
229              
230 50 100 100     388 if( $mod eq '?' || $mod =~ m{^optional\[}i) {
    100 100        
    100 100        
    100          
    50          
    100          
231 17         106 $attr{optional} = 1;
232             } elsif( $mod eq '!' || $mod =~ m{^required?\[}i) {
233 6         39 $attr{optional} = 0;
234             } elsif( $mod eq '@' || $mod =~ m{^array\[}i) {
235 22         149 $attr{array} = 1;
236             } elsif( $mod eq '%' ) {
237 2   33     23 $attr{hash} //= $conf->{hash_delimiter};
238             } elsif( $mod =~ m{^skipundef\[}i) {
239 0         0 $attr{skipundef}= 1;
240             } elsif( $mod eq '~' ) {
241 2         4 $attr{skipundef}= 1;
242 2         12 $attr{optional} = 1;
243             }
244             }
245              
246 515 100       1482 if( exists $conf->{types}{ $type } ) {
247 513         730 for my $key ( keys %{$conf->{types}{ $type }} ) {
  513         1609  
248 1423 100       2927 next if defined $attr{ $key };
249 1413         2831 $attr{ $key } = $conf->{types}{ $type }{ $key };
250             }
251             } else {
252 2         30 die sprintf 'Type "%s" is not defined', $type;
253             }
254             }
255              
256             # Preload module if required
257 526 100       1451 if( my $load = $attr{load} ) {
258 98 100       310 if( 'CODE' eq ref $load ) {
    100          
259 1         5 $load->($self, $name);
260             } elsif( 'ARRAY' eq ref $load ) {
261 54         104 for my $module ( @$load ) {
262 108         265 my $e = load_class( $module );
263 108 50       408218 die $e if $e;
264             }
265             } else {
266 43         141 my $e = load_class( $load );
267 43 50       408405 die $e if $e;
268             }
269             }
270              
271             # Get value
272 526         401239 my @input;
273 526 100       1709 if( $attr{jpath} ) {
    100          
    100          
274             # JSON Pointer
275 19   50     74 $vars->{json} //= Mojolicious::Plugin::Vparam::JSON::parse_json(
      100        
276             $self->req->body // ''
277             );
278 19 100       1275 if( $vars->{json} ) {
279             $vars->{pointer} //=
280 18   66     83 Mojo::JSON::Pointer->new( $vars->{json} );
281 18 100       153 if( $vars->{pointer}->contains( $attr{jpath} ) ) {
282 14         469 my $value = $vars->{pointer}->get( $attr{jpath} );
283 14 100       362 @input = 'ARRAY' eq ref $value ? @$value : $value;
284             }
285             }
286             } elsif( $attr{cpath} ) {
287             # CSS
288 6   50     26 $vars->{dom} //= Mojolicious::Plugin::Vparam::DOM::parse_dom(
      100        
289             $self->req->body // ''
290             );
291 6 100       29 if( $vars->{dom} ) {
292             @input = $vars->{dom}->find( $attr{cpath} )
293 5         29 ->map('text')->each;
294             }
295             } elsif( $attr{xpath} ) {
296 5   50     34 $vars->{xml} //= Mojolicious::Plugin::Vparam::XML::parse_xml(
      33        
297             $self->req->body // ''
298             );
299 1 50       4 if( $vars->{xml} ) {
300 0         0 @input = map {$_->textContent}
301 0         0 $vars->{xml}->findnodes( $attr{xpath} );
302             }
303             } else {
304             # POST parameters
305 496         1581 @input = params($self, $name);
306             }
307              
308             # Set undefined value if parameter not set
309             # if array or hash then keep it empty
310             @input = (undef)
311             if
312             not @input
313             and not $attr{array}
314             and not $attr{hash}
315 522 100 100     83932 ;
      100        
316              
317             # Set array if values more that one
318 522 100 100     1635 $attr{array} = 1 if @input > 1 and not $attr{hash};
319              
320 522 100       1358 if( $attr{multiline} ) {
321 9 50       24 if( $attr{array} ) {
322 0         0 die 'Array of arrays not supported';
323             } else {
324             my $regexp = 'Regexp' eq ref $attr{multiline}
325             ? $attr{multiline}
326 9 100       37 : qr{\r?\n}
327             ;
328             # Apply multiline
329             @input =
330 17         61 grep { $_ =~ m{\S} }
331 9         19 map { split $regexp, $_ } @input;
  9         95  
332             }
333              
334             # Multiline force array
335 9         21 $attr{array} = 1;
336             }
337              
338             # Normalize hash key delimiter
339             $attr{hash} = $conf->{hash_delimiter}
340 522 100 100     1538 if $attr{hash} && $attr{hash} eq '1';
341              
342             # Process on all input values
343 522         844 my @keys;
344             my @output;
345 522         1400 for my $index ( 0 .. $#input ) {
346 564         1064 my $in = my $out = $input[$index];
347              
348 564         800 $out = $in;
349              
350 564         745 my $key;
351 564 100       1409 if( $attr{hash} ) {
352 28         284 my @splitted = split $attr{hash}, $out, 2;
353 28 100       82 unless( @splitted == 2 ) {
354 3         28 $self->verror(
355             $name,
356             %attr,
357             index => $index,
358             in => $in,
359             out => $out,
360             message => 'Not a hash',
361             );
362 3         9 next;
363             }
364              
365 25         33 $key = $splitted[0];
366 25         43 $out = $splitted[1];
367             }
368              
369             # Apply pre filter
370 561 100       2696 $out = $attr{pre}->( $self, $out, \%attr ) if $attr{pre};
371              
372             # Apply validator
373 561 100       13710 if( $attr{valid} ) {
374 548 100       1462 if( my $error = $attr{valid}->($self, $out, \%attr) ) {
375             # Set default value if error
376 175         1750 $out = $attr{default};
377              
378             # Default value always supress error
379 175 100       537 $error = 0 if exists $attr{default};
380             # Disable error on optional
381 175 100       420 if( $attr{optional} ) {
382             # Only if input param not set
383 17 100       57 $error = 0 if not defined $in;
384 17 100 100     108 $error = 0 if defined($in) and $in =~ m{^\s*$};
385             }
386              
387             $self->verror(
388 175 100       1416 $name,
389             %attr,
390             index => $index,
391             in => $in,
392             out => $out,
393             message => $error,
394             ) if $error;
395             }
396             }
397              
398             # Hack for bool values:
399             # HTML forms do not transmit if checkbox off
400 561 100 100     2687 if( $type and not defined $in ) {
401 59 100       158 if( $type eq 'bool' ) {
402             $out = exists $attr{default}
403             ? $attr{default}
404 6 100       19 : 0
405             ;
406             }
407 59 100       142 if( $type eq 'logic' ) {
408 6         12 $out = $attr{default};
409             }
410             }
411              
412             # Apply post filter
413 561 100       1803 $out = $attr{post}->( $self, $out, \%attr ) if $attr{post};
414              
415             # Apply other filters
416 561         4940 for my $key ( keys %attr ) {
417             # Skip unknown attribute
418 4627 100       9114 next unless $conf->{filters}{ $key };
419              
420             my $error = $conf->{filters}{ $key }->(
421 77         249 $self, $out, $attr{ $key }
422             );
423 77 100       245 if( $error ) {
424             # Set default value if error
425 38         68 $out = $attr{default};
426              
427             # Default value always supress error
428 38 100       92 $error = 0 if defined $attr{default};
429             # Disable error on optional
430 38 100       87 if( $attr{optional} ) {
431             # Only if input param not set
432 1 50       3 $error = 0 if not defined $in;
433 1 50 33     8 $error = 0 if defined($in) and $in =~ m{^\s*$};
434             }
435              
436             $self->verror(
437 38 100       267 $name,
438             %attr,
439             index => $index,
440             in => $in,
441             out => $out,
442             message => $error,
443             ) if $error;
444             }
445             }
446              
447             # Add output
448 561 100 100     2002 if( defined($out) or not $attr{skipundef} ) {
449 553         967 push @output, $out;
450 553 100 66     1822 push @keys, $key if $attr{hash} and defined $key;
451             }
452             }
453              
454             # Error for required empty array
455             $self->verror( $name, %attr, message => 'Empty array' )
456 522 100 100     1743 if $attr{array} and not $attr{optional} and not @input;
      100        
457             # Error for required empty hash
458             $self->verror( $name, %attr, message => 'Empty hash' )
459 522 100 66     1485 if $attr{hash} and not $attr{optional} and not @input;
      100        
460              
461             # Rename for model
462 522   66     1883 my $as = $attr{as} // $name;
463              
464 522 100       1333 if( $attr{hash} ) {
    100          
465 12         37 $result{ $as } = { mesh @keys, @output };
466             } elsif( $attr{array} ) {
467 42 100       139 if( defined $attr{multijoin} ) {
468             $result{ $as } = @output
469 7 100       18 ? join $attr{multijoin}, grep {defined} @output
  15         40  
470             : undef
471             ;
472             } else {
473 35         95 $result{ $as } = \@output;
474             }
475             } else {
476             $result{ $as } = $output[0]
477 468 100 66     1657 unless $attr{skipundef} and not defined($output[0]);
478             }
479             # Mojolicious::Validator::Validation
480             $self->validation->output->{$name} = $result{ $as }
481 522 50       2419 if $conf->{mojo_validator};
482             }
483              
484 454 100       27981 return wantarray ? %result : \%result;
485 74         1735 });
486              
487             # One parameter
488             $app->helper(vparam => sub{
489 429     429   1149078 my ($self, $name, $def, %attr) = @_;
490              
491 429 50       1323 die 'Parameter name required' unless defined $name;
492 429 50       1001 die 'Parameter type or definition required' unless defined $def;
493              
494 429         645 my $result;
495              
496 429 50       1343 unless( %attr ) {
    50          
    100          
    50          
    100          
497 319         1544 $result = $self->vparams( $name => $def );
498 0         0 } elsif( 'HASH' eq ref $def ) {
499             # Ignore attrs not in HashRef
500 0         0 $result = $self->vparams( $name => $def );
501 0         0 } elsif( 'Regexp' eq ref $def ) {
502 2         12 $result = $self->vparams( $name => { regexp => $def, %attr } );
503 0         0 } elsif('CODE' eq ref $def) {
504 0         0 $result = $self->vparams( $name => { post => $def, %attr } );
505 0         0 } elsif('ARRAY' eq ref $def) {
506 0         0 $result = $self->vparams( $name => { in => $def, %attr } );
507             } else {
508 108         792 $result = $self->vparams( $name => { type => $def, %attr } );
509             }
510              
511 424   33     3669 return $result->{ $attr{as} // $name };
512 74         1422 });
513              
514             # Load extensions: types, filters etc.
515 74         1209 for my $module (find_modules 'Mojolicious::Plugin::Vparam') {
516 1258         331906 my $e = load_class( $module );
517 1258 50       25613 die $e if $e;
518              
519 1258 100       12966 next unless $module->can('register');
520 1184         3767 $module->register($self, $app, $conf);
521             }
522              
523 74         605 return;
524             }
525              
526             1;
527              
528             __END__
529              
530             =encoding utf-8
531              
532             =head1 NAME
533              
534             Mojolicious::Plugin::Vparam - Mojolicious plugin validator for GET/POST data.
535              
536             =head1 DESCRIPTION
537              
538             Features:
539              
540             =over
541              
542             =item *
543              
544             Simple syntax or full featured
545              
546             =item *
547              
548             Many predefined types
549              
550             =item *
551              
552             Shortcuts for the most common uses
553              
554             =item *
555              
556             Filters complementary types
557              
558             =item *
559              
560             Support arrays and hashes of values
561              
562             =item *
563              
564             Support HTML checkbox as bool
565              
566             =item *
567              
568             Simple JSON values extraction and validation using JSON Pointer from
569             L<Mojo::JSON::Pointer>.
570              
571             =item *
572              
573             Simple XML/HTML values extraction and validation using CSS selector engine
574             from L<Mojo::DOM::CSS> or XPath from L<XML::LibXML>.
575              
576             =item *
577              
578             Validate all parameters at once and get hash to simple use in any Model
579              
580             =item *
581              
582             Manage validation errors
583              
584             =item *
585              
586             Full Mojolicious::Validator::Validation integration
587              
588             =back
589              
590             This module use simple parameters types B<str>, B<int>, B<email>, B<bool>,
591             etc. to validate.
592             Instead of many other modules you mostly not need add specific validation
593             subs or rules.
594             Just set parameter type. But if you want sub or regexp you can do it too.
595              
596             =head1 SYNOPSIS
597              
598             # Add plugin in startup
599             $self->plugin('Vparam');
600              
601             # Use in controller
602             $login = $self->vparam(login => 'str');
603             $passw = $self->vparam(password => 'password', size => [8, 100]);
604             $email = $self->vparam(email => 'email', optional => 1);
605             $session = $self->vparam(session => 'bool', default => 1);
606              
607             $ids = $self->vparam(ids => '@int');
608              
609             =head1 METHODS
610              
611             =head2 vparam
612              
613             Get one parameter. By default parameter is required.
614              
615             # Simple get one parameter
616             $param1 = $self->vparam(date => 'datetime');
617              
618             # Or more syntax
619             $param2 = $self->vparam(count => {type => 'int', default => 1});
620             # Or more simple syntax
621             $param2 = $self->vparam(count => 'int', default => 1);
622              
623             =head2 vparams
624              
625             Get many parameters as hash. By default all parameters are required.
626              
627             %params = $self->vparams(
628             # Simple syntax
629             name => 'str',
630             password => qr{^\w{8,32}$},
631             myparam => sub { $_[1] && $_[1] eq 'ok' ? 1 : 0 } },
632             someone => ['one', 'two', 'tree'],
633              
634             # More syntax
635             from => { type => 'date', default => '' },
636             to => { type => 'date', default => '' },
637             id => { type => 'int' },
638             money => { regexp => qr{^\d+(?:\.\d{2})?$} },
639             myparam => { post => sub { $_[1] && $_[1] eq 'ok' ? 1 : 0 } },
640              
641             # Checkbox
642             isa => { type => 'bool', default => 0 },
643             );
644              
645             =head2 vsort
646              
647             Like I<vparams> but add some keys to simple use with tables. Example:
648              
649             # HTML - table with controls and filters
650             Order by:
651             <select name="oby">
652             <option value="0">Name</option>
653             <option value="1">Date</option>
654             </select>
655             Order direction:
656             <select name="ods">
657             <option value="asc">Low to High</option>
658             <option value="desc">High to Low</option>
659             </select>
660             Count per page:
661             <select name="rws">
662             <option value="10">10</option>
663             <option value="100">100</option>
664             </select>
665             Filter by name:
666             <input type="text" name="name" value="">
667             Any other filters ...
668              
669              
670             # Controller
671             %params = $self->vsort(
672             -sort => ['name', 'date', ...],
673              
674             # next as vparams
675             name => 'text',
676             ...
677             );
678              
679             =over
680              
681             =item page
682              
683             Page number. Default: 1.
684              
685             You can set different name by I<vsort_page> config parameter.
686             If you set undef then parameter is not apply.
687              
688             =item rws
689              
690             Rows on page. Default: 25.
691              
692             You can set different name by I<vsort_rws> config parameter.
693             You can set different default by I<vsort_rows> config parameter.
694             If you set undef then parameter is not apply.
695              
696             =item oby
697              
698             Column number for sorting. Default: 1 - in many cases first database
699             column is primary key.
700              
701             You can set different name by I<vsort_oby> config parameter.
702             If you set undef then parameter is not apply.
703              
704             Value of B<oby> The value will be automatically mapped to the column name
705             using the L</-sort> attribute.
706             Also, the value will be checked for proper mapping.
707             So you do not need to worry about it.
708              
709             =item ods
710              
711             Sort order ASC|DESC. Default: ASC.
712              
713             You can set different name by I<vsort_ods> config parameter.
714             If you set undef then parameter is not apply.
715              
716             =back
717              
718             =head2 verror $name
719              
720             Get parameter error string. Return 0 if no error.
721              
722             # Get error
723             print $self->verror('myparam');
724              
725             # Get error for first element in array
726             print $self->verror('myparam' => 0);
727              
728             # Set error
729             $self->verror('myparam', message => 'Error message')
730              
731             =head2 verrors
732              
733             Return errors count in scalar context. In list context return errors hash.
734              
735             # List context get hash
736             my %errors = $self->verrors;
737              
738             # Scalar context get count
739             die 'Errors!' if $self->verrors;
740              
741             =head2 vclass $name, @classes
742              
743             Get classes for invalid input. Return empty string if no error.
744              
745             # Form example
746             <input name="myparam" class="<%= vclass 'myparam' %>">
747             # Return next code for invalid I<myparam>:
748             # <input name="myparam" class="field-with-error">
749              
750             You can set additional I<@classes> to set if field invalid.
751              
752             =head2 vvalue $name, $default
753              
754             Get raw input value after validation. Return I<$default> value or empty
755             string before validation.
756              
757             # Form example:
758             <input name="myparam" value="<%= vvalue 'myparam' %>">
759              
760             # Return next code if user just open form without submit and validation:
761             # <input name="myparam" value="">
762              
763             # Then user submit form and you validate id. For example user submit "abc":
764             # <input name="myparam" value="abc">
765              
766             =head2 vtype $name, %opts
767              
768             Set new type $name if defined %opts. Else return type $name definition.
769              
770             # Get type
771             $self->vtype('mytype');
772              
773             # Set type
774             # load - requires modules
775             # pre - get int
776             # valid - check for not empty
777             # post - force number
778             $self->vtype('mytype',
779             pre => sub {
780             my ($self, $param) = @_;
781             return int $param // '';
782             },
783             valid => sub {
784             my ($self, $param) = @_;
785             return length $param ? 0 : 'Invalid'
786             },
787             post => sub {
788             my ($self, $param) = @_;
789             return 0 + $param;
790             }
791             );
792              
793             =head2 vfilter $name, &sub
794              
795             Set new filter $name if defined %opts. Else return filter $name definition.
796              
797             # Get filter
798             $self->vfilter('myfilter');
799              
800             # Set filter
801             $self->vfilter('myfilter', sub {
802             my ($self, $param, $expression) = @_;
803             return $param eq $expression ? 0 : 'Invalid';
804             });
805              
806             Filter sub must return 0 if parameter value is valid. Or error string if not.
807              
808             =head1 SIMPLE SYNTAX
809              
810             You can use the simplified syntax instead of specifying the type,
811             simply by using an expression instead.
812              
813             =over
814              
815             =item I<REGEXP>
816              
817             Apply as L</regexp> filter. No type verification, just match.
818              
819             $self->vparam(myparam => qr{^(abc|cde)$});
820              
821             =item I<CODE> $mojo, $value
822              
823             Apply as L</post> function. You need manual verify and set error.
824              
825             $self->vparam(myparam => sub { $_[1] && $_[1] eq 'good' ? 1 : 0 });
826              
827             =item I<ARRAY>
828              
829             Apply as L</in> filter. No type verification, just match.
830              
831             $self->vparam(myparam => [qw(abc cde)]);
832              
833             =back
834              
835             =head1 CONFIGURATION
836              
837             =over
838              
839             =item class
840              
841             CSS class for invalid parameters. Default: field-with-error.
842              
843             =item types
844              
845             You can simple add you own types.
846             Just set this parameters as HashRef with new types definition.
847              
848             =item filters
849              
850             You can simple add you own filters.
851             Just set this parameters as HashRef with new filters definition.
852              
853              
854             =item vsort_page
855              
856             Parameter name for current page number in I<vsort>. Default: page.
857              
858             =item vsort_rws
859              
860             Parameter name for current page rows count in I<vsort>. Default: rws.
861              
862             =item rows
863              
864             Default rows count for I<vsort_rws>. Default: 25.
865              
866             =item vsort_oby
867              
868             Parameter name for current order by I<vsort>. Default: oby.
869              
870             =item vsort_ods
871              
872             Parameter name for current order destination I<vsort>. Default: ods.
873              
874             =item ods
875              
876             Default order destination for I<vsort_rws>. Default: ASC.
877              
878             =item phone_country
879              
880             Phone country. Default: empty.
881              
882             =item phone_region
883              
884             Phone region. Default: empty.
885              
886             =item phone_fix
887              
888             Name of algorithm to fix phone, typicallty country code. Default: empty.
889              
890             =item date
891              
892             Date format for strftime. Default: %F.
893             if no format specified, return L<DateTime> object.
894              
895             =item time
896              
897             Time format for strftime. Default: %T.
898             if no format specified, return L<DateTime> object.
899              
900             =item datetime
901              
902             Datetime format for strftime. Default: '%F %T %z'.
903             if no format specified, return L<DateTime> object.
904              
905             =item blessed
906              
907             By default return objects used for parse or validation:
908             L<Mojo::URL>, L<DateTime>, etc.
909              
910             =item optional
911              
912             By default all parameters are required. You can change this by set this
913             parameter as true.
914              
915             =item address_secret
916              
917             Secret for address:points signing. Format: "ADDRESS:LATITUDE,LONGITUDE[MD5]".
918             MD5 ensures that the coordinates belong to address.
919              
920             =item password_min
921              
922             Minimum password length. Default: 8.
923              
924             =item hash_delimiter
925              
926             Delimiter to split input parameter on two parts: key and value.
927             Default: => - like a perl hash.
928              
929             =item mojo_validator
930              
931             Enable L<Mojolicious::Validator::Validation> integration.
932              
933             =back
934              
935             =cut
936              
937             =head1 TYPES
938              
939             List of supported types:
940              
941             =head2 int
942              
943             Signed integer. Use L</min> filter for unsigned.
944              
945             =head2 numeric or number
946              
947             Signed number. Use L</min> filter for unsigned.
948              
949             =head2 money
950              
951             Get money. Use L</min> filter for unsigned.
952              
953             =head2 percent
954              
955             Unsigned number: 0 <= percent <= 100.
956              
957              
958             =head2 str
959              
960             Trimmed text. Must be non empty if required.
961              
962             =head2 text
963              
964             Any text. No errors.
965              
966             =head2 password
967              
968             String with minimum length from I<password_min>.
969             Must content characters and digits.
970              
971             =head2 uuid
972              
973             Standart 32 length UUID. Return in lower case.
974              
975             =head2 date
976              
977             Get date. Parsed from many formats.
978             See I<date> configuration parameter for result format.
979             See L<DateTime::Format::DateParse> and even more.
980              
981             =head2 time
982              
983             Get time. Parsed from many formats.
984             See I<time> configuration parameter for result format.
985             See L<DateTime::Format::DateParse> and even more.
986              
987             =head2 datetime
988              
989             Get full date and time. Parsed from many formats.
990             See I<datetime> configuration parameter for result format.
991              
992             Input formats:
993              
994             =over
995              
996             =item *
997              
998             Timestamp.
999              
1000             =item *
1001              
1002             Relative from now in format C<[+-] DD HH:MM:SS>. First sign required.
1003              
1004             =over
1005              
1006             =item *
1007              
1008             Minutes by default. Example: C<+15> or C<-6>.
1009              
1010             =item *
1011              
1012             Minutes and seconds. Example: C<+15:44>.
1013              
1014             =item *
1015              
1016             Hours. Example: C<+3:15:44>.
1017              
1018             =item *
1019              
1020             Days. Example: C<+8 3:15:44>.
1021              
1022             =back
1023              
1024             Values are given in arbitrary range.
1025             For example you can add 400 minutes and 300 seconds: C<+400:300>.
1026              
1027             =item *
1028              
1029             All that can be obtained L<DateTime::Format::DateParse>.
1030              
1031             =item *
1032              
1033             Russian date format like C<DD.MM.YYYY>
1034              
1035             =back
1036              
1037             =head2 bool
1038              
1039             Boolean value. Can be used to get value from checkbox or another sources.
1040              
1041             HTML forms do not send checbox if it checked off.
1042             You need always set default value to supress error if checkbox not checked:
1043              
1044             $self->vparam(mybox => 'bool', default => 0);
1045              
1046             Valid values are:
1047              
1048             =over
1049              
1050             =item
1051              
1052             I<TRUE> can be 1, yes, true, ok
1053              
1054             =item
1055              
1056             I<FALSE> can be 0, no, false, fail
1057              
1058             =item
1059              
1060             Empty string is I<FALSE>
1061              
1062             =back
1063              
1064             Other values get error.
1065              
1066             Example:
1067              
1068             <input type="checkbox" name="bool1" value="yes">
1069             <input type="checkbox" name="bool2" value="1">
1070             <input type="checkbox" name="bool3" value="ok">
1071              
1072             =head2 logic
1073              
1074             Three-valued logic. Same as I<bool> but undef state if empty string. Example:
1075              
1076             <select name="logic1">
1077             <option value=""></option>
1078             <option value="1">True</option>
1079             <option value="0">False</option>
1080             </select>
1081              
1082             =head2 email
1083              
1084             Email adress.
1085              
1086             =head2 url
1087              
1088             Get url as L<Mojo::URL> object.
1089              
1090             =head2 phone
1091              
1092             Phone in international format. Support B<wait>, B<pause> and B<additional>.
1093              
1094             You can set default country I<phone_country> and region I<phone_country> codes.
1095             Then you users can input shortest number.
1096             But this is not work if you site has i18n.
1097              
1098             =head2 json
1099              
1100             JSON incapsulated as form parameter.
1101              
1102             =head2 address
1103              
1104             Location address. Two forms are parsed: string and json.
1105             Can verify adress sign to trust source.
1106              
1107             =head2 lon
1108              
1109             Longitude.
1110              
1111             =head2 lat
1112              
1113             Latilude.
1114              
1115             =head2 isin
1116              
1117             International Securities Identification Number:
1118             Mir, American Express, Diners Club, JCB, Visa,
1119             MasterCard, Maestro, etc.
1120              
1121             You can check for ISIN type like:
1122              
1123             # Mir
1124             $self->vparam(card => 'isin', regexp => qr{^2});
1125              
1126             # American Express, Diners Club, JCB
1127             $self->vparam(card => 'isin', regexp => qr{^3});
1128              
1129             # Visa
1130             $self->vparam(card => 'isin', regexp => qr{^4});
1131              
1132             # MasterCard
1133             $self->vparam(card => 'isin', regexp => qr{^5});
1134              
1135             # Maestro
1136             $self->vparam(card => 'isin', regexp => qr{^6});
1137              
1138             =head2 maestro
1139              
1140             Some local country, not 16 numbers cards: Maestro, Visa Electron, etc.
1141              
1142             =head3 creditcard
1143              
1144             Aggregate any creditcard: ISIN, Maestro, etc.
1145              
1146             =head2 barcode
1147              
1148             Barcode: EAN-13, EAN-8, EAN 5, EAN 2, UPC-12, ITF-14, JAN, UPC, etc.
1149              
1150             $self->vparam(barcode => 'goods');
1151              
1152             =head2 inn
1153              
1154             RU: Taxpayer Identification Number
1155              
1156             =head2 kpp
1157              
1158             RU: Code of reason for registration
1159              
1160             =head1 ATTRIBUTES
1161              
1162             You can set a simple mode as in example or full mode. Full mode keys:
1163              
1164              
1165             =head2 default
1166              
1167             Default value. Default: undef.
1168              
1169             # Supress myparam to be undefined and error
1170             $self->vparam(myparam => 'str', default => '');
1171              
1172             =head2 load
1173              
1174             Autoload module for this type.
1175             Can be module name, array of module names or sub.
1176              
1177             =head2 pre $mojo, &sub
1178              
1179             Incoming filter sub. Used for primary filtration: string length and trim, etc.
1180             Result will be used as new param value.
1181              
1182             Usually, if you need this attribute, you need to create a new type.
1183              
1184             =head2 valid $mojo, &sub
1185              
1186             Validation sub. Return 0 if valid, else string of error.
1187              
1188             Usually, if you need this attribute, you need to create a new type.
1189              
1190             =head2 post $mojo, &sub
1191              
1192             Out filter sub. Used to modify value for use in you program. Usually used to
1193             bless in some object.
1194             Result will be used as new param value.
1195              
1196             =head2 type
1197              
1198             Parameter type. If set then some filters will be apply. See L</TYPES>.
1199              
1200             $self->vparam(myparam => 'datetime');
1201             $self->vparam(myparam => {type => 'datetime'});
1202             $self->vparams(
1203             myparam1 => {type => 'datetime'},
1204             myparam2 => {isa => 'datetime'},
1205             );
1206              
1207             After the application of the type used filters.
1208              
1209             You can use B<isa> alias instead of B<type>.
1210              
1211             =head2 as
1212              
1213             Rename output key for I<vparams> to simple use in models.
1214              
1215             # Return {login => '...', password => '...'}
1216             $self->vparams(
1217             myparam1 => {type => 'str', as => 'login'},
1218             myparam2 => {type => 'str', as => 'password'},
1219             );
1220              
1221             =head2 array
1222              
1223             Force value will array. Default: false.
1224              
1225             You can force values will arrays by B<@> prefix or case insensive B<array[...]>.
1226              
1227             # Arrays shortcut syntax
1228             $param1 = $self->vparam(array1 => '@int');
1229             $param2 = $self->vparam(array2 => 'array[int]');
1230              
1231             # Array attribute syntax
1232             $param3 = $self->vparam(array3 => 'int', array => 1);
1233              
1234             # The array will come if more than one value incoming
1235             # Example: http://mysite.us?array4=123&array4=456...
1236             $param4 = $self->vparam(array4 => 'int');
1237              
1238             =head2 hash
1239              
1240             Get value as hash. Default: false.
1241             You can get values as hash by B<%> prefix.
1242              
1243             To make hash vparam split value by predefined delimiter
1244             (see I<hash_delimiter> configuration parameter). It`s important to make
1245             value format.
1246              
1247             # Get param1 as hash. Parameter value need to be like 'a=>1'.
1248             $param1 = $self->vparam(myparam1 => '%int');
1249              
1250             # Same, but custom delimiter "::"
1251             $param2 = $self->vparam(myparam2 => 'int', hash => '::');
1252              
1253             # Multiple parameters supported.
1254             # You can send many myparam3 with different values list like:
1255             # ?myparam3=a_1&myparam3=b_2&myparam3=c_3
1256             # and get perl hash like $param3 = {a => 1, b => 3, c => 3}
1257             $param3 = $self->vparam(myparam3 => '%int', hash => '_');
1258              
1259             =head2 optional
1260              
1261             By default all parameters are required. You can change this for parameter by
1262             set I<optional>.
1263             Then true and value is not passed validation don`t set verrors.
1264              
1265             # Simple vparam
1266             # myparam is undef but no error.
1267             $param1 = $self->vparam(param1 => 'int', optional => 1);
1268              
1269             # Set one in vparams
1270             %params = $self->vparams(
1271             myparam => { type => 'int', optional => 1 },
1272             );
1273              
1274             # Set all in vparams
1275             %params = $self->vparams(
1276             -optional => 1,
1277             param1 => 'int',
1278             param2 => 'str',
1279             );
1280              
1281             # Shortcut optional syntax
1282             $param2 = $self->vparam(param2 => '?int');
1283             $param3 = $self->vparam(param3 => 'maybe[int]');
1284             $param4 = $self->vparam(param4 => 'optional[int]');
1285              
1286             # Shortcut required syntax
1287             $param5 = $self->vparam(param5 => '!int');
1288             $param6 = $self->vparam(param6 => 'require[int]');
1289             $param7 = $self->vparam(param7 => 'required[int]');
1290              
1291             =head2 skip
1292              
1293             So as not to smear the validation code you can use the I<skip> parameter
1294             to skip on the condition.
1295             This attribute is useful for controlling access to the form fields.
1296              
1297             # This example don`t get param1 in production mode.
1298              
1299             # HTML
1300             % unless( $self->app->mode eq 'production' ) {
1301             %= number_field 'param1'
1302             % }
1303              
1304             # Simple flag
1305             $param1 = $self->vparam(
1306             param1 => 'int', skip => $self->app->mode eq 'production',
1307             );
1308              
1309             # Same as by use sub.
1310             $param1 = $self->vparam(
1311             param1 => 'int', skip => sub { $_[0]->app->mode eq 'production' },
1312             );
1313              
1314             If you use sub then first parameter is controller.
1315              
1316             =head2 skipundef
1317              
1318             By default all parameters are in output hash. You can skip parameter in result
1319             if it`s undefined by set I<skipundef>.
1320              
1321             # Simple vparam
1322             # myparam is undef.
1323             $param1 = $self->vparam(param1 => 'int', optional => 1, skipundef => 1);
1324              
1325             # Simple flag
1326             # The %params hash is empty if myparam value is not integer.
1327             %params = $self->vparams(
1328             myparam => { type => 'int', optional => 1, skipundef => 1 },
1329             );
1330              
1331             # Set all in vparams
1332             # The %params hash is empty if all parameters are not valid.
1333             %params = $self->vparams(
1334             -skipundef => 1,
1335             param1 => 'int',
1336             param2 => 'str',
1337             );
1338              
1339             # Shortcut syntax: skipundef and optional is on
1340             $param2 = $self->vparam(param2 => '~int');
1341              
1342             Arrays always return as arrayref. But undefined values will be skipped:
1343              
1344             # This vparam return [1,2,3] for ?param3=1&param3=&param3=2&param3=3
1345             $param2 = $self->vparam(param3 => '~int');
1346              
1347             =head2 multiline
1348              
1349             You can simple split I<textarea> to values:
1350              
1351             # This vparam return [1,2,3] for input "1\n2\n3\n"
1352             $param1 = $self->vparam(param1 => 'int', multiline => 1);
1353              
1354             # Or by custom regexp
1355             # This vparam return [1,2,3] for input "1,2,3"
1356             $param1 = $self->vparam(param1 => 'int', multiline => qr{\s*,\s*});
1357              
1358             Empty lines ignored.
1359              
1360             =head2 multijoin
1361              
1362             Any array values can be joined in string:
1363              
1364             # This vparam return "1,2,3" for input ?param1=1&param1=2&param1=3
1365             $param1 = $self->vparam(param1 => 'int', multijoin => ',');
1366              
1367             # This vparam return "1,2,3" for input "1\n2\n3\n"
1368             $param2 = $self->vparam(param2 => 'int', multiline => 1, multijoin => ',');
1369              
1370             =head2 blessed
1371              
1372             Keep and return blessed object for parsed parameters if available.
1373             Vparam always return scalars if disabled.
1374              
1375             Note: if defined I<date>, I<time>, I<datetime> then always return
1376             formatted scalar.
1377              
1378             =head2 jpath
1379              
1380             If you POST data not form but raw JSON you can use JSON Pointer selectors
1381             from L<Mojo::JSON::Pointer> to get and validate parameters.
1382              
1383             # POST data contains:
1384             # {"point":{"address":"some", "lon": 45.123456, "lat": 38.23452}}
1385              
1386             %opts = $self->vparams(
1387             address => { type => 'str', jpath => '/point/address' },
1388             lon => { type => 'lon', jpath => '/point/lon' },
1389             lat => { type => 'lat', jpath => '/point/lat' },
1390             );
1391              
1392             Note: we don`t support multikey in json. Use hash or die.
1393              
1394             =head2 cpath
1395              
1396             Same as jpath but parse XML/HTML using CSS selectors from L<Mojo::DOM::CSS>.
1397              
1398             # POST data contains:
1399             # <Point>
1400             # <Address>some</Address>
1401             # <Lon>45.123456</Lon>
1402             # <Lat>38.23452</Lat>
1403             # </Point>
1404              
1405             %opts = $self->vparams(
1406             address => { type => 'str', cpath => 'Point > Address' },
1407             lon => { type => 'lon', cpath => 'Point > Lon' },
1408             lat => { type => 'lat', cpath => 'Point > Lat' },
1409             );
1410              
1411              
1412             =head2 xpath
1413              
1414             Same as cpath but parse XML/HTML using XPath selectors from L<XML::LibXML>.
1415              
1416             # POST data contains:
1417             # <Point time="2016-11-25 14:39:00 +0300">
1418             # <Address>some</Address>
1419             # <Lon>45.123456</Lon>
1420             # <Lat>38.23452</Lat>
1421             # </Point>
1422              
1423             %opts = $self->vparams(
1424             address => { type => 'str', xpath => '/Point/Address' },
1425             lon => { type => 'lon', xpath => '/Point/Lon' },
1426             lat => { type => 'lat', xpath => '/Point/Lat' },
1427             time => { type => 'datetime', xpath => '/Point/@time' },
1428             );
1429              
1430             =head1 RESERVED ATTRIBUTES
1431              
1432             =head2 -sort
1433              
1434             List of column names for I<vsort>. Usually not all columns visible for users and
1435             you need convert column numbers in names. This also protect you SQL queries
1436             from set too much or too low column number.
1437              
1438             =head2 -optional
1439              
1440             Set default I<optional> flag for all params in L</vparams> and L</vsort>.
1441              
1442             =head2 -skipundef
1443              
1444             Set default I<skipundef> flag for all params in L</vparams> and L</vsort>.
1445              
1446             =head1 FILTERS
1447              
1448             Filters are used in conjunction with types for additional verification.
1449              
1450             =head2 range
1451              
1452             Check parameter value to be in range.
1453              
1454             # Error if myparam less than 10 or greater than 100
1455             $self->vparam(myparam => 'int', range => [10, 100]);
1456              
1457             =head2 regexp
1458              
1459             Check parameter to be match for regexp
1460              
1461             # Error if myparam not equal "abc" or "cde"
1462             $self->vparam(myparam => 'str', regexp => qr{^(abc|cde)$});
1463              
1464             =head2 in
1465              
1466             Check parameter value to be in list of defined values.
1467              
1468             # Error if myparam not equal "abc" or "cde"
1469             $self->vparam(myparam => 'str', in => [qw(abc cde)]);
1470              
1471             =head2 size
1472              
1473             Check maximum length in utf8.
1474              
1475             # Error if value is an empty string
1476             $self->vparam(myparam => 'str', size => [1, 100]);
1477              
1478             =head2 Numbers comparation
1479              
1480             I<min>, I<max>, I<equal>, I<not>
1481              
1482             =head2 Strings comparation
1483              
1484             I<lt>, I<gt>, I<le>, I<ge>, I<cmp>, I<eq>, I<ne>
1485              
1486             =head1 RESTRICTIONS
1487              
1488             =over
1489              
1490             =item *
1491              
1492             Version 1.0 invert I<valid> behavior: now checker return 0 if no error
1493             or description string if has.
1494              
1495             =item *
1496              
1497             New errors keys: orig => in, pre => out
1498              
1499             =back
1500              
1501             =head1 SEE ALSO
1502              
1503             L<Mojolicious::Validator::Validation>, L<Mojolicious::Plugin::Human>.
1504              
1505             =head1 AUTHORS
1506              
1507             Dmitry E. Oboukhov <unera@debian.org>,
1508             Roman V. Nikolaev <rshadow@rambler.ru>
1509              
1510             =head1 COPYRIGHT
1511              
1512             Copyright (C) 2011 Dmitry E. Oboukhov <unera@debian.org>
1513             Copyright (C) 2011 Roman V. Nikolaev <rshadow@rambler.ru>
1514              
1515             This library is free software; you can redistribute it and/or modify
1516             it under the same terms as Perl itself, either Perl version 5.8.8 or,
1517             at your option, any later version of Perl 5 you may have available.
1518              
1519             =cut