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