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