bdecd33b404948ff8c654da1ce896e7604ebd2d2
[catagits/Catalyst-Plugin-FormValidator-Simple.git] / lib / Catalyst / Plugin / FormValidator / Simple.pm
1 package Catalyst::Plugin::FormValidator::Simple;
2 use strict;
3 use base qw/Catalyst::Plugin::FormValidator/;
4 # doesn't use parent module at all, but this is required for Catalyst::Plugin::FillInForm
5
6 use MRO::Compat;
7 require FormValidator::Simple;
8
9 our $VERSION = '0.14';
10
11 sub setup {
12     my $self = shift;
13     $self->maybe::next::method(@_);
14     my $setting = $self->config->{validator};
15     my $plugins = $setting && exists $setting->{plugins}
16         ? $setting->{plugins}
17         : [];
18         FormValidator::Simple->import(@$plugins);
19     if ( $setting && exists $setting->{messages} ) {
20         FormValidator::Simple->set_messages( $setting->{messages} );
21     }
22     if ( $setting && exists $setting->{options} ) {
23         FormValidator::Simple->set_option( %{ $setting->{options} } );
24     }
25     if ( $setting && exists $setting->{message_format} ) {
26         FormValidator::Simple->set_message_format( $setting->{message_format} );
27     }
28     if ( $setting && exists $setting->{message_decode_from} ) {
29         FormValidator::Simple->set_message_decode_from( $setting->{message_decode_from} );
30     }
31 }
32
33 sub prepare {
34     my $c = shift;
35     $c = $c->maybe::next::method(@_);
36     $c->{validator} = FormValidator::Simple->new;
37     return $c;
38 }
39
40 sub form {
41     my $c = shift;
42     if ($_[0]) {
43         my $form = $_[1] ? [@_] : $_[0];
44         $c->{validator}->check($c->req, $form);
45     }
46     return $c->{validator}->results;
47 }
48
49 sub set_invalid_form {
50     my $c = shift;
51     $c->{validator}->set_invalid(@_);
52     return $c->{validator}->results;
53 }
54
55 1;
56 __END__
57
58 =head1 NAME
59
60 Catalyst::Plugin::FormValidator::Simple - Validator for Catalyst with FormValidator::Simple
61
62 =head1 SYNOPSIS
63
64     use Catalyst qw/FormValidator::Simple FillInForm/;
65
66     # set option
67     MyApp->config->{validator} = {
68         plugins => ['CreditCard', 'Japanese'],
69         options => { charset => 'euc'},
70     }
71
72 in your controller
73
74     sub defaulti : Private {
75
76         my ($self, $c) = @_;
77
78         $c->form(
79             param1 => [qw/NOT_BLANK ASCII/, [qw/LENGTH 4 10/]],
80             param2 => [qw/NOT_BLANK/, [qw/JLENGTH 4 10/]],
81             mail1  => [qw/NOT_BLANK EMAIL_LOOSE/],
82             mail2  => [qw/NOT_BLANK EMAIL_LOOSE/],
83             { mail => [qw/mail1 mail2/] } => ['DUPLICATION'],
84         );
85
86         print $c->form->valid('param1');
87
88         if ( some condition... ) {
89
90             $c->form(
91                 other_param => [qw/NOT_INT/],
92             );
93         }
94
95         if ( some condition... ) {
96
97             # set your original invalid type.
98             $c->set_invalid_form( param3 => 'MY_ERROR' );
99
100         }
101
102         if ( $c->form->has_error ) {
103             
104             if ( $c->form->missing('param1') ) {
105                 ...
106             }
107
108             if ( $c->form->invalid( param1 => 'ASCII' ) ) {
109                 ...
110             }
111
112             if ( $c->form->invalid( param3 => 'MY_ERROR' ) ) {
113                 ...
114             }
115
116         }
117     }
118
119 =head1 DESCRIPTION
120
121 This plugin allows you to validate request parameters with FormValidator::Simple.
122 See L<FormValidator::Simple> for more information.
123
124 This behaves like as L<Catalyst::Plugin::FormValidator>.
125
126 =head1 CONFIGURATION
127
128 set config with 'validator' key.
129
130     MyApp->config->{validator} = { ... };
131
132 or
133
134     MyApp->config(
135         validator => { ... },
136     );
137
138 =head2 PLUGINS
139
140 If you want to use some plugins for FormValidator::Simple, you can set like following.
141
142     MyApp->config(
143         validator => {
144             plugins => [qw/Japanese CreditCard DBIC::Unique/],
145         },
146     );
147
148 In this example, FormValidator::Simple::Plugin::Japanese, FormValidator::Simple::Plugin::CreditCard,
149 and FormValidator::Simple::Plugin::DBIC::Unique are loaded.
150
151 =head2 OPTIONS
152
153 When you set some options needed by specific validations, do like this.
154
155     MyApp->config(
156         validator => {
157             plugins => [qw/Japanese CreditCard DBIC::Unique/],
158             options => {
159                 charset => 'euc',
160                 dbic_base_class => 'MyApp::Model::DBIC',
161             },
162         },
163     );
164
165 'charset' is necessary for Plugin::Japanese, and 'dbic_cbase_class' is used in Plugin::DBIC::Unique.
166
167 =head1 VALIDATION
168
169 use 'form' method, see L<FormValidator::Simple> in detail.
170
171     sub do_add : Local {
172         my ( $self, $c ) = @_;
173
174         # execute validation.
175         $c->form(
176             name  => [qw/NOT_BLANK ASCII/,       [qw/LENGTH 0 20/] ],
177             email => [qw/NOT_BLANK EMAIL_LOOSE/, [qw/LENGTH 0 20/] ],
178             { unique => [qw/name email/] } => [qw/DBIC_UNIQUE User name email/],
179         );
180
181         if ( ... ) {
182
183             # execute validation one more time in specific condition.
184             $c->form(
185                 ...                 
186             );
187
188         }
189
190         # See Catalyst::Plugin::RequestToken for '$c->validate_token'
191         if ( $c->validate_token ) {
192
193             # you can force to set invalid data.
194             $c->set_invalid_form( token => 'TOKEN' );
195
196         }
197
198         # check result.
199         # you can pick up result-object with 'form' method
200
201         my $result = $c->form;
202
203         if ( $result->has_error ) {
204
205         # this is same as
206         # if ( $result->has_missing or $result->has_invalid )
207
208             $c->detach('add');
209
210         }
211
212     }
213
214 =head1 HANDLING SUCCESSFUL RESULT
215
216 After it passes all validations, you may wanna put input-data into database.
217 It's a elegant way to use [ L<Class::DBI> and L<Class::DBI::FromForm> ] or [ L<DBIx::Class> and L<DBIx::Class::WebForm> ].
218
219     sub do_add : Local {
220         my ( $self, $c ) = @_;
221
222         $c->form(
223             name  => [qw/NOT_BLANK/],
224             email => [qw/NOT_BLANK/],
225         );
226
227         my $result = $c->form;
228         if ( $result->has_error ) {
229             $c->detach('add');
230         }
231
232         my $user = MyProj::Model::DBIC::User->create_from_form($result);
233         
234         # this behaves like this...
235         # MyProj::Model::DBIC::User->create({
236         #    name  => $result->valid('name'),
237         #    email => $result->valid('email'),
238         # });
239         #
240         # if the key exists as the table's column, set the value with 'valid'
241     }
242
243 Here, I explain about 'valid' method. If the value indicated with key-name passes validations,
244 You can get the data with 'valid',
245
246     my $result = $c->form(
247         name  => [qw/NOT_BLANK/],
248         email => [qw/NOT_BLANK/],
249     ); 
250
251     print $result->valid('name');
252
253     print $result->valid('email');
254
255 But, this is for only single key validation normally.
256
257     my $result = $c->form(
258         name => [qw/NOT_BLANK/], # single key validation
259         { mail_dup => [qw/email email2/] } => ['DUPLICATION'] # multiple keys one
260     );
261
262     print $result->valid('name'); # print out the value of 'name'
263
264     print $result->valid('mail_dup'); # no value.
265
266 There are exceptions. These are 'DATETIME', 'DATE'.
267
268     my $result = $c->form(
269         { created_on => [qw/created_year created_month created_day/] }
270         =>
271         [qw/DATETIME/],
272     );
273
274     print $result->valid('created_on'); #print out datetime string like "2005-11-23 00:00:00".
275
276 If you set some class around datetime in configuration. It returns object of the class you indicate.
277 You can choose from L<Time::Piece> and L<DateTime>. For example...
278
279     MyApp->config(
280         validator => {
281             plugins => [...],
282             options => {
283                 datetime_class => 'Time::Piece',
284             },
285         },
286     );
287
288 or
289
290     MyApp->config(
291         validator => {
292             plugins => [...],
293             options => {
294                 datetime_class => 'DateTime',
295                 time_zone      => 'Asia/Tokyo',
296             },
297         },
298     );
299
300 then
301
302     my $result = $c->form(
303         { created_on => [qw/created_year created_month created_day/] }
304         =>
305         [qw/DATETIME/],
306     );
307
308     my $dt = $result->valid('created_on');
309
310     print $dt->ymd;
311
312     MyProj::Model::CDBI::User->create_from_form($result);
313
314 This may be useful when you define 'has_a' relation for datetime columns.
315 For example, in your table class inherits 'Class::DBI'
316
317     __PACKAGE__->has_a( created_on => 'DateTime',
318         inflate => ...,
319         deflate => ...,
320     );
321
322 And see also L<Class::DBI::Plugin::TimePiece>, L<Class::DBI::Plugin::DateTime>.
323
324 =head1 MESSAGE HANDLING
325
326 in template file, you can handle it in detail.
327
328     [% IF c.form.has_error %]
329     <p>Input Error</p>
330     <ul>
331     [% IF c.form.missing('name') %]
332     <li>input name!</li>
333     [% END %]
334     [% IF c.form.invalid('name') %]
335     <li>name is wrong</li>
336     [% END %]
337     [% IF c.form.invalid('name', 'ASCII') %]
338     <li>input name with ascii code.</li>
339     [% END %]
340     [% IF c.form.invalid('name', 'LENGTH') %]
341     <li>wrong length for name.</li>
342     [% END %]
343     </ul>
344     [% END %]
345
346 or, make it more easy.
347
348     [% IF c.form.has_error %]
349     <p>Input Error</p>
350     <ul>
351     [% FOREACH key IN c.form.error %]
352         [% FOREACH type IN c.form.error(key) %]
353         <li>Invalid: [% key %] - [% type %]</li>
354         [% END %]
355     [% END %]
356     </li>
357     [% END %]
358
359 And you can also use messages configuration as hash reference.
360
361     MyApp->config(
362         validator => {
363             plugins  => [...],
364             messages => {
365                 user => {
366                     name => {
367                         NOT_BLANK => 'Input name!',
368                         ASCII     => 'Input name with ascii code!',
369                     },
370                     email => {
371                         DEFAULT   => 'email is wrong.!',
372                         NOT_BLANK => 'input email.!'
373                     },
374                 },
375                 company => {
376                     name => {
377                         NOT_BLANK => 'Input name!',
378                     },
379                 },
380             },
381         },
382     );
383
384 or YAML file. set file name
385
386     MyApp->config(
387         validator => {
388             plugins  => [...],
389             messages => 'conf/messages.yml',
390         },
391     );
392
393 and prepare yaml file like following,
394
395     DEFAULT:
396         name:
397             DEFAULT: name is invalid
398     user:
399         name:
400             NOT_BLANK: Input name!
401             ASCII: Input name with ascii code!
402         email:
403             DEFAULT: Email is wrong!
404             NOT_BLANK: Input email!
405     company:
406         name:
407             NOT_BLANK: Input name!
408
409 the format is...
410
411     Action1_Name:
412         Key1_Name:
413             Validation1_Name: Message
414             Validation2_Name: Message
415         Key2_Name:
416             Validation1_Name: Message
417     Action2_Name:
418         Key1_Name:
419             ...
420         
421 After messages configuration, call messages() method from result-object.
422 and set action-name as argument.
423
424     [% IF c.form.has_error %]
425     <ul>
426         [% FOREACH message IN c.form.messages('user') %]
427         <li>[% message %]</li>
428         [% END %]
429     </ul>
430     [% END %]
431
432 you can set each message format
433
434     MyApp->config(
435         validator => {
436             messages => 'messages.yml',  
437             message_format => '<p>%s</p>'
438         },
439     );
440
441     [% IF c.form.has_error %]
442         [% c.form.messages('user').join("\n") %]
443     [% END %]
444
445 =head1 SEE ALSO
446
447 L<FormValidator::Simple>
448
449 L<Catalyst>
450
451 =head1 AUTHOR
452
453 Lyo Kato E<lt>lyo.kato@gmail.comE<gt>
454
455 =head1 COPYRIGHT AND LICENSE
456
457 Copyright(C) 2005 by Lyo Kato
458
459 This library is free software; you can redistribute it and/or
460 modify it under the same terms as Perl itself.
461
462 =cut
463