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