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