Applied doc patch from martins to improve the formfu config
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Tutorial / AdvancedCRUD / FormFu.pod
CommitLineData
b80f58e5 1=head1 NAME
2
f279297a 3
4
b80f58e5 5Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Part 9: Advanced CRUD - FormFu
6
7
c010ae0d 8NOTE: This part of the tutorial is in progress and will be ready soon.
9
b80f58e5 10=head1 OVERVIEW
11
12This is B<Part 9 of 10> for the Catalyst tutorial.
13
14L<Tutorial Overview|Catalyst::Manual::Tutorial>
15
16=over 4
17
18=item 1
19
20L<Introduction|Catalyst::Manual::Tutorial::Intro>
21
22=item 2
23
24L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
25
26=item 3
27
28L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics>
29
30=item 4
31
32L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
33
34=item 5
35
36L<Authentication|Catalyst::Manual::Tutorial::Authentication>
37
38=item 6
39
40L<Authorization|Catalyst::Manual::Tutorial::Authorization>
41
42=item 7
43
44L<Debugging|Catalyst::Manual::Tutorial::Debugging>
45
46=item 8
47
48L<Testing|Catalyst::Manual::Tutorial::Testing>
49
50=item 9
51
d682d02d 52B<Advanced CRUD::FormFu>
b80f58e5 53
54=item 10
55
56L<Appendices|Catalyst::Manual::Tutorial::Appendices>
57
58=back
59
60
61=head1 DESCRIPTION
62
cca5cd98 63This portion of the tutorial explores L<HTML::FormFu|HTML::FormFu> and
64how it can be used to manage forms, perform validation of form input,
65as well as save and restore data to/from the database.
66
67See
68L<Catalyst::Manual::Tutorial::AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
69for additional form management options other than
70L<HTML::FormFu|HTML::FormFu>.
71
72
73=head1 Install C<HTML::FormFu>
74
75If you are following along in Ubuntu, it turns out that C<HTML::FormFu>
76is not yet available as a package at the time this was written. To
77install it with a combination of C<apt-get> packages and traditional
78CPAN modules, first use C<apt-get> to install most of the modules
79required by C<HTML::FormFu>:
80
81 sudo apt-get install libtest-nowarnings-perl libdatetime-format-builder-perl \
82 libdatetime-format-strptime-perl libdatetime-locale-perl \
83 libhtml-tokeparser-simple-perl liblist-moreutils-perl \
84 libregexp-copy-perl libregexp-common-perl libyaml-syck-perl libparams-util-perl
85
86Then use the following command to install directly from CPAN the modules
87that aren't available as Ubuntu/Debian packages via C<apt-get>:
88
89 sudo cpan File::ShareDir Task::Weaken Config::Any HTML::FormFu \
90 Catalyst::Controller::HTML::FormFu
91
92
93=head1 C<HTML::FormFu> FORM CREATION
94
95This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to
96add additional functionality to the manually created form from Part 4.
97
98
99=head2 Inherit From C<Catalyst::Controller::HTML::FormFu>
100
101First, change your C<lib/MyApp/Controller/Books.pm> to inherit from
102L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu>
103by changing the C<use base> line from the default of:
104
105 use base 'Catalyst::Controller';
106
107to use the FormFu base controller class:
108
109 use base 'Catalyst::Controller::HTML::FormFu';
110
111
112=head2 Add Action to Display and Save the Form
113
114Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
115following method:
116
d682d02d 117 =head2 formfu_create
cca5cd98 118
59d6a035 119 Use HTML::FormFu to create a new book
cca5cd98 120
121 =cut
122
d682d02d 123 sub formfu_create :Local :FormConfig {
cca5cd98 124 my ($self, $c) = @_;
125
126 # Get the form that the :FormConfig attribute saved in the stash
127 my $form = $c->stash->{form};
128
166c90a0 129 # Check if the form has been submitted (vs. displaying the initial
130 # form) and if the data passed validation. "submitted_and_valid"
cca5cd98 131 # is shorthand for "$form->submitted && !$form->has_errors"
132 if ($form->submitted_and_valid) {
133 # Create a new book
134 my $book = $c->model('DB::Books')->new_result({});
135 # Save the form data for the book
136 $form->save_to_model($book);
137 # Set a status message for the user
138 $c->flash->{status_msg} = 'Book created';
139 # Return to the books list
140 $c->response->redirect($c->uri_for('list'));
141 $c->detach;
9cb64a37 142 } else {
143 # Get the authors from the DB
144 my @authorObjs = $c->model("DB::Authors")->all();
145 # Create an array of arrayrefs where each arrayref is an author
146 my @authors;
147 foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) {
148 push(@authors, [$_->id, $_->last_name]);
149 }
150 # Get the select added by the config file
151 my $select = $form->get_element({type => 'Select'});
152 # Add the authors to it
153 $select->options(\@authors);
154 }
155
cca5cd98 156 # Set the template
d682d02d 157 $c->stash->{template} = 'books/formfu_create.tt2';
cca5cd98 158 }
159
160
161=head2 Create a Form Config File
162
163Although C<HTML::FormFu> supports any configuration file handled by
164L<Config::Any|Config::Any>, most people tend to use YAML. First
165create a directory to hold your form configuration files:
166
167 mkdir -p root/forms/books
168
d682d02d 169Then create the file C<root/forms/books/formfu_create.yml> and enter the
cca5cd98 170following text:
171
172 ---
9cb64a37 173 # indicator is the field that is used to test for form submission
cca5cd98 174 indicator: submit
9cb64a37 175 # Start listing the form elements
cca5cd98 176 elements:
9cb64a37 177 # The first element will be a text field for the title
cca5cd98 178 - type: Text
179 name: title
180 label: Title
9cb64a37 181 # This is an optional 'mouse over' title pop-up
cca5cd98 182 attributes:
183 title: Enter a book title here
d682d02d 184
185 # Another text field for the numeric rating
cca5cd98 186 - type: Text
187 name: rating
188 label: Rating
189 attributes:
190 title: Enter a rating between 1 and 5 here
d682d02d 191
192 # Add a drop-down list for the author selection. Note that we will
193 # dynamically fill in all the authors from the controller but we
194 # could manually set items in the drop-list by adding this YAML code:
195 # options:
196 # - [ '1', 'Bastien' ]
197 # - [ '2', 'Nasseh' ]
198 - type: Select
199 name: authors
200 label: Author
201
9cb64a37 202 # The submit button
cca5cd98 203 - type: Submit
204 name: submit
205 value: Submit
206
0171092f 207B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
1fba2369 208tricky. See the L<Config::General Config for this tutorial> section of
209this document for a more foolproof config format.
0171092f 210
cca5cd98 211
212=head2 Update the CSS
213
214Edit C<root/src/ttsite.css> and add the following lines to the bottom of
215the file:
216
d682d02d 217 input {
cca5cd98 218 display: block;
219 }
d682d02d 220 select {
cca5cd98 221 display: block;
222 }
d682d02d 223 .submit {
224 padding-top: .5em;
225 display: block;
cca5cd98 226 }
227
d682d02d 228These changes will display form elements vertically. Note that the
229existing definition of the C<.error> class is pulling the color scheme
230settings from the C<root/lib/config/col> file that was created by the
231TTSite helper. This allows control over the CSS color settings from a
232single location.
cca5cd98 233
234
235=head2 Create a Template Page To Display The Form
236
d682d02d 237Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
cca5cd98 238
239 [% META title = 'Create/Update Book' %]
240
241 [%# Render the HTML::FormFu Form %]
242 [% form %]
243
8a7c5151 244 <p><a href="[% c.uri_for('list') %]">Return to book list</a></p>
cca5cd98 245
246
247=head2 Add Links for Create and Update via C<HTML::FormFu>
248
249Open C<root/src/books/list.tt2> in your editor and add the following to
250the bottom of the existing file:
251
252 <p>
253 HTML::FormFu:
8a7c5151 254 <a href="[% c.uri_for('formfu_create') %]">Create</a>
cca5cd98 255 </p>
256
d682d02d 257This adds a new link to the bottom of the book list page that we can
258use to easily launch our HTML::FormFu-based form.
259
cca5cd98 260
d682d02d 261=head2 Test The <HTML::FormFu> Create Form
cca5cd98 262
263Press C<Ctrl-C> to kill the previous server instance (if it's still
264running) and restart it:
265
266 $ script/myapp_server.pl
267
0909d46f 268Login as C<test01> (password: mypass). Once at the Book List page,
269click the new HTML::FormFu "Create" link at the bottom to display the
270form. Fill in the following values: Title = "Internetworking with
271TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit,
272and you will be returned to the Book List page with a "Book created"
273status message displayed.
d682d02d 274
275Also note that this implementation allows you to can create books with
276bogus information. Although we have constrained the authors with the
59d6a035 277drop-down list (note that this isn't bulletproof because we still have
278not prevented a user from "hacking" the form to specify other values),
279there are no restrictions on items such as the length of the title (for
280example, you can create a one-letter title) and value for the rating
281(you can use any number you want, and even non-numeric values with
282SQLite). The next section will address this concern.
cca5cd98 283
284B<Note:> Depending on the database you are using and how you established
285the columns in your tables, the database could obviously provide various
286levels of "type enforcement" on your data. The key point being made in
287the previous paragraph is that the I<web application> itself is not
288performing any validation.
289
290
291=head1 C<HTML::FormFu> VALIDATION AND FILTERING
292
d682d02d 293Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section
294did provide an automated mechanism to build the form, the real power of
295this module stems from functionality that can automatically validate and
296filter the user input. Validation uses constraints to be sure that
297users input appropriate data (for example, that the email field of a
298form contains a valid email address). Filtering can also be used to
299remove extraneous whitespace from fields or to escape meta-characters in
300user input.
cca5cd98 301
cca5cd98 302
d682d02d 303=head2 Add Constraints
cca5cd98 304
d682d02d 305Open C<root/forms/books/formfu_create.yml> in your editor and update it
cca5cd98 306to match:
307
308 ---
9cb64a37 309 # indicator is the field that is used to test for form submission
cca5cd98 310 indicator: submit
9cb64a37 311 # Start listing the form elements
cca5cd98 312 elements:
9cb64a37 313 # The first element will be a text field for the title
cca5cd98 314 - type: Text
315 name: title
316 label: Title
9cb64a37 317 # This is an optional 'mouse over' title pop-up
cca5cd98 318 attributes:
319 title: Enter a book title here
9cb64a37 320 # Add constraints for the field
cca5cd98 321 constraints:
59d6a035 322 # Force the length to be between 5 and 40 chars
cca5cd98 323 - type: Length
d682d02d 324 min: 5
59d6a035 325 max: 40
9cb64a37 326 # Override the default of 'Invalid input'
59d6a035 327 message: Length must be between 5 and 40 characters
f279297a 328
d682d02d 329 # Another text field for the numeric rating
cca5cd98 330 - type: Text
331 name: rating
332 label: Rating
333 attributes:
334 title: Enter a rating between 1 and 5 here
d682d02d 335 # Use Filter to clean up the input data
336 filter:
d682d02d 337 # Remove everything except digits
338 - NonNumeric
339 # Add constraints to the field
cca5cd98 340 constraints:
f279297a 341 - Required
9cb64a37 342 # Make sure it's a number
cca5cd98 343 - Integer
f279297a 344 message: "Digits only, please."
345 # Filters apply before constraints.
346 # If a user gives the rating "excellent", the NonNumeric filter would remove the entire string as it contains no digits.
347 # Remove the NonNumeric filter and let the Integer constraint handle the validation and error message.
d682d02d 348
349 # Add a select list for the author selection. Note that we will
350 # dynamically fill in all the authors from the controller but we
351 # could manually set items in the select by adding this YAML code:
352 # options:
353 # - [ '1', 'Bastien' ]
354 # - [ '2', 'Nasseh' ]
355 - type: Select
356 name: authors
357 label: Author
358 # Convert the drop-down to a multi-select list
359 multiple: 1
360 # Display 3 entries (user can scroll to see others)
361 size: 3
362 # One could argue we don't need to do filters or constraints for
363 # a select list, but it's smart to do validation and sanity
364 # checks on this data in case a user "hacks" the input
d682d02d 365 # Add constraints to the field
366 constraints:
d682d02d 367 # Make sure it's a number
368 - Integer
369
9cb64a37 370 # The submit button
cca5cd98 371 - type: Submit
372 name: submit
373 value: Submit
d682d02d 374
f279297a 375 # Global filters and constraints.
cca5cd98 376 constraints:
f279297a 377 # The user cannot leave any fields blank
378 - Required
379 # If not all fields are required, move the Required constraint to the fields that are.
380 filter:
381 # Remove whitespace at both ends
382 - TrimEdges
383 # Escape HTML characters for safety
384 - HTMLEscape
cca5cd98 385
0171092f 386B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
1fba2369 387tricky. See the L<Config::General Config for this tutorial> section of
388this document for a more foolproof config format.
0171092f 389
d682d02d 390The main changes are:
391
392=over 4
393
394=item *
395
396The C<Select> element for C<authors> is changed from a single-select
397drop-down to a multi-select list by adding configuration for the
398C<multiple> and C<size> options in C<formfu_create.yml>.
399
400=item *
401
402Constraints are added to provide validation of the user input. See
403L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other
404constraints that are available.
405
406=item *
407
408A variety of filters are run on every field to remove and escape
409unwanted input. See L<HTML::FormFu::Filter|HTML::FormFu::Filter>
410for more filter options.
411
412=back
413
414
415=head2 Try Out the Updated Form
416
417Press C<Ctrl-C> to kill the previous server instance (if it's still
418running) and restart it:
419
420 $ script/myapp_server.pl
421
59d6a035 422Make sure you are still logged in as C<test01> and try adding a book
423with various errors: title less than 5 characters, non-numeric rating, a
424rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
425When you click Submit, the HTML::FormFu C<constraint> items will
426validate the logic and insert feedback as appropriate. Try adding blank
427spaces at the front or the back of the title and note that it will be
428removed.
429
430
431=head1 CREATE AND UPDATE/EDIT ACTION
432
433Let's expand the work done above to add an edit action. First, open
434C<lib/MyApp/Controller/Books.pm> and add the following method to the
435bottom:
436
437 =head2 formfu_edit
438
439 Use HTML::FormFu to update an existing book
440
441 =cut
442
443 sub formfu_edit :Local :FormConfig('books/formfu_create.yml') {
444 my ($self, $c, $id) = @_;
445
446 # Get the specified book
447 my $book = $c->model('DB::Books')->find($id);
448
449 # Make sure we were able to get a book
450 unless ($book) {
451 $c->flash->{error_msg} = "Invalid book -- Cannot edit";
452 $c->response->redirect($c->uri_for('list'));
453 $c->detach;
454 }
455
456 # Get the form that the :FormConfig attribute saved in the stash
457 my $form = $c->stash->{form};
458
166c90a0 459 # Check if the form has been submitted (vs. displaying the initial
460 # form) and if the data passed validation. "submitted_and_valid"
59d6a035 461 # is shorthand for "$form->submitted && !$form->has_errors"
462 if ($form->submitted_and_valid) {
463 # Save the form data for the book
464 $form->save_to_model($book);
465 # Set a status message for the user
466 $c->flash->{status_msg} = 'Book edited';
467 # Return to the books list
468 $c->response->redirect($c->uri_for('list'));
469 $c->detach;
470 } else {
471 # Get the authors from the DB
472 my @authorObjs = $c->model("DB::Authors")->all();
473 # Create an array of arrayrefs where each arrayref is an author
474 my @authors;
475 foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) {
476 push(@authors, [$_->id, $_->last_name]);
477 }
478 # Get the select added by the config file
479 my $select = $form->get_element({type => 'Select'});
480 # Add the authors to it
481 $select->options(\@authors);
482 # Populate the form with existing values from DB
483 $form->defaults_from_model($book);
484 }
485
486 # Set the template
487 $c->stash->{template} = 'books/formfu_create.tt2';
488 }
489
490Most of this code should look familiar to what we used in the
491C<formfu_create> method (in fact, we should probably centralize some of
492the common code in separate methods). The main differences are:
493
494=over 4
495
496=item *
497
498We accept C<$id> as an argument via the URL.
499
500=item *
501
502We use C<$id> to look up the existing book from the database.
503
504=item *
505
506We make sure the C<$id> and book lookup returned a valid book. If not,
507we set the error message and return to the book list.
508
509=item *
510
511If the form has been submitted and passes validation, we skip creating a
512new book and just use C<$form-E<gt>save_to_model> to update the existing
513book.
514
515=item *
516
517If the form is being displayed for the first time (or has failed
518validation and it being redisplayed), we use
519 C<$form-E<gt>default_from_model> to populate the form with data from the
520database.
521
522=back
523
524Then, edit C<root/src/books/list.tt2> and add a new link below the
525existing "Delete" link that allows us to edit/update each existing book.
526The last E<lt>tdE<gt> cell in the book list table should look like the
527following:
528
529 <td>
530 [% # Add a link to delete a book %]
8a7c5151 531 <a href="[% c.uri_for('delete', book.id) %]">Delete</a>
59d6a035 532 [% # Add a link to edit a book %]
8a7c5151 533 <a href="[% c.uri_for('formfu_edit', book.id) %]">Edit</a>
59d6a035 534 </td>
535
536
537=head2 Try Out the Edit/Update Feature
538
539Press C<Ctrl-C> to kill the previous server instance (if it's still
540running) and restart it:
541
542 $ script/myapp_server.pl
543
544Make sure you are still logged in as C<test01> and go to the
545L<http://localhost:3000/books/list> URL in your browser. Click the
546"Edit" link next to "Internetworking with TCP/IP Vol. II", change the
547rating to a 3, the "II" at end of the title to the number "2", add
548Stevens as a co-author (control-click), and click Submit. You will then
549be returned to the book list with a "Book edited" message at the top in
550green. Experiment with other edits to various books.
d682d02d 551
cca5cd98 552
1fba2369 553=head2 Config::General Config for this tutorial
554
555If you are having difficulty with YAML config above, please save the
556below into the file C<formfu_create.conf> and delete the
557C<formfu_create.yml> file. The below is in
558L<Config::General|Config::General> format which follows the syntax of
559Apache config files.
560
561 constraints Required
562 <elements>
1fba2369 563 <constraints>
564 min 5
565 max 40
566 type Length
567 message Length must be between 5 and 40 characters
568 </constraints>
569 filter TrimEdges
570 filter HTMLEscape
571 name title
572 type Text
573 label Title
574 <attributes>
575 title Enter a book title here
576 </attributes>
577 </elements>
578 <elements>
1fba2369 579 constraints Integer
580 filter TrimEdges
581 filter NonNumeric
582 name rating
583 type Text
584 label Rating
585 <attributes>
586 title Enter a rating between 1 and 5 here
587 </attributes>
588 </elements>
589 <elements>
590 constraints Integer
591 filter TrimEdges
592 filter HTMLEscape
593 name authors
594 type Select
595 label Author
596 multiple 1
597 size 3
598 </elements>
599 <elements>
600 value Submit
601 name submit
602 type Submit
603 </elements>
604 indicator submit
605
606
0171092f 607
cca5cd98 608=head1 AUTHOR
609
610Kennedy Clark, C<hkclark@gmail.com>
611
612Please report any errors, issues or suggestions to the author. The
613most recent version of the Catalyst Tutorial can be found at
614L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Manual/lib/Catalyst/Manual/Tutorial/>.
615
d682d02d 616Copyright 20066-2008, Kennedy Clark, under Creative Commons License
cca5cd98 617(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
0909d46f 618