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