3 Catalyst::Manual::Tutorial::09_AdvancedCRUD::09_FormFu - Catalyst Tutorial - Chapter 9: Advanced CRUD - FormFu
8 This is B<Chapter 9 of 10> for the Catalyst tutorial.
10 L<Tutorial Overview|Catalyst::Manual::Tutorial>
16 L<Introduction|Catalyst::Manual::Tutorial::01_Intro>
20 L<Catalyst Basics|Catalyst::Manual::Tutorial::02_CatalystBasics>
24 L<More Catalyst Basics|Catalyst::Manual::Tutorial::03_MoreCatalystBasics>
28 L<Basic CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD>
32 L<Authentication|Catalyst::Manual::Tutorial::05_Authentication>
36 L<Authorization|Catalyst::Manual::Tutorial::06_Authorization>
40 L<Debugging|Catalyst::Manual::Tutorial::07_Debugging>
44 L<Testing|Catalyst::Manual::Tutorial::08_Testing>
48 B<09_Advanced CRUD::09_FormFu>
52 L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
59 This portion of the tutorial explores L<HTML::FormFu|HTML::FormFu> and
60 how it can be used to manage forms, perform validation of form input,
61 as well as save and restore data to/from the database. This was written
62 using HTML::FormFu version 0.05001.
65 L<Catalyst::Manual::Tutorial::09_AdvancedCRUD|Catalyst::Manual::Tutorial::09_AdvancedCRUD>
66 for additional form management options other than
67 L<HTML::FormFu|HTML::FormFu>.
70 =head1 HTML::FormFu FORM CREATION
72 This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to
73 add additional functionality to the manually created form from Chapter 4.
76 =head2 Inherit From Catalyst::Controller::HTML::FormFu
78 First, change your C<lib/MyApp/Controller/Books.pm> to inherit from
79 L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu>
80 by changing the C<use parent> line from the default of:
82 use parent 'Catalyst::Controller';
84 to use the FormFu base controller class:
86 use parent 'Catalyst::Controller::HTML::FormFu';
89 =head2 Add Action to Display and Save the Form
91 Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
96 Use HTML::FormFu to create a new book
100 sub formfu_create :Chained('base') :PathPart('formfu_create') :Args(0) :FormConfig {
103 # Get the form that the :FormConfig attribute saved in the stash
104 my $form = $c->stash->{form};
106 # Check if the form has been submitted (vs. displaying the initial
107 # form) and if the data passed validation. "submitted_and_valid"
108 # is shorthand for "$form->submitted && !$form->has_errors"
109 if ($form->submitted_and_valid) {
111 my $book = $c->model('DB::Book')->new_result({});
112 # Save the form data for the book
113 $form->model->update($book);
114 # Set a status message for the user
115 $c->flash->{status_msg} = 'Book created';
116 # Return to the books list
117 $c->response->redirect($c->uri_for($self->action_for('list')));
120 # Get the authors from the DB
121 my @author_objs = $c->model("DB::Author")->all();
122 # Create an array of arrayrefs where each arrayref is an author
124 foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
125 push(@authors, [$_->id, $_->last_name]);
127 # Get the select added by the config file
128 my $select = $form->get_element({type => 'Select'});
129 # Add the authors to it
130 $select->options(\@authors);
134 $c->stash->{template} = 'books/formfu_create.tt2';
138 =head2 Create a Form Config File
140 Although C<HTML::FormFu> supports any configuration file handled by
141 L<Config::Any|Config::Any>, most people tend to use YAML. First
142 create a directory to hold your form configuration files:
144 mkdir -p root/forms/books
146 Then create the file C<root/forms/books/formfu_create.yml> and enter the
150 # indicator is the field that is used to test for form submission
152 # Start listing the form elements
154 # The first element will be a text field for the title
158 # This is an optional 'mouse over' title pop-up
160 title: Enter a book title here
162 # Another text field for the numeric rating
167 title: Enter a rating between 1 and 5 here
169 # Add a drop-down list for the author selection. Note that we will
170 # dynamically fill in all the authors from the controller but we
171 # could manually set items in the drop-list by adding this YAML code:
173 # - [ '1', 'Bastien' ]
174 # - [ '2', 'Nasseh' ]
184 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
185 tricky. See the L<Config::General Config for this tutorial> section of
186 this document for a more foolproof config format.
189 =head2 Update the CSS
191 Edit C<root/static/css/main.css> and add the following lines to the bottom of
206 These changes will display form elements vertically. Note that the
207 existing definition of the C<.error> class is pulling the color scheme
208 settings from the C<root/lib/config/col> file that was created by the
209 TTSite helper. This allows control over the CSS color settings from a
213 =head2 Create a Template Page To Display The Form
215 Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
217 [% META title = 'Create/Update Book' %]
219 [%# Render the HTML::FormFu Form %]
222 <p><a href="[% c.uri_for(c.controller.action_for('list')) %]">Return to book list</a></p>
225 =head2 Add Links for Create and Update via C<HTML::FormFu>
227 Open C<root/src/books/list.tt2> in your editor and add the following to
228 the bottom of the existing file:
233 <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a>
236 This adds a new link to the bottom of the book list page that we can
237 use to easily launch our HTML::FormFu-based form.
240 =head2 Test The HTML::FormFu Create Form
242 Press C<Ctrl-C> to kill the previous server instance (if it's still
243 running) and restart it:
245 $ script/myapp_server.pl
247 Login as C<test01> (password: mypass). Once at the Book List page,
248 click the new HTML::FormFu "Create" link at the bottom to display the
249 form. Fill in the following values:
251 Title: Internetworking with TCP/IP Vol. II
255 Click the Submit button, and you will be returned to the Book List page
256 with a "Book created" status message displayed.
258 Also note that this implementation allows you to create books with any
259 bogus information. Although we have constrained the authors with the
260 drop-down list (note that this isn't bulletproof because we still have
261 not prevented a user from "hacking" the form to specify other values),
262 there are no restrictions on items such as the length of the title (for
263 example, you can create a one-letter title) and the value of the rating
264 (you can use any number you want, and even non-numeric values with
265 SQLite). The next section will address this concern.
267 B<Note:> Depending on the database you are using and how you established
268 the columns in your tables, the database could obviously provide various
269 levels of "type enforcement" on your data. The key point being made in
270 the previous paragraph is that the I<web application> itself is not
271 performing any validation.
274 =head1 HTML::FormFu VALIDATION AND FILTERING
276 Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section
277 did provide an automated mechanism to build the form, the real power of
278 this module stems from functionality that can automatically validate and
279 filter the user input. Validation uses constraints to be sure that
280 users input appropriate data (for example, that the email field of a
281 form contains a valid email address). Filtering can also be used to
282 remove extraneous whitespace from fields or to escape meta-characters in
286 =head2 Add Constraints
288 Open C<root/forms/books/formfu_create.yml> in your editor and update it
292 # indicator is the field that is used to test for form submission
294 # Start listing the form elements
296 # The first element will be a text field for the title
300 # This is an optional 'mouse over' title pop-up
302 title: Enter a book title here
303 # Add constraints for the field
305 # Force the length to be between 5 and 40 chars
309 # Override the default of 'Invalid input'
310 message: Length must be between 5 and 40 characters
312 # Another text field for the numeric rating
317 title: Enter a rating between 1 and 5 here
318 # Use Filter to clean up the input data
319 # Could use 'NonNumeric' below, but since Filters apply *before*
320 # constraints, it would conflict with the 'Integer' constraint below.
321 # So let's skip this and just use the constraint.
323 # Remove everything except digits
325 # Add constraints to the field
327 # Make sure it's a number
329 message: "Required. Digits only, please."
330 # Check the min & max values
334 message: "Must be between 1 and 5."
336 # Add a select list for the author selection. Note that we will
337 # dynamically fill in all the authors from the controller but we
338 # could manually set items in the select by adding this YAML code:
340 # - [ '1', 'Bastien' ]
341 # - [ '2', 'Nasseh' ]
345 # Convert the drop-down to a multi-select list
347 # Display 3 entries (user can scroll to see others)
349 # One could argue we don't need to do filters or constraints for
350 # a select list, but it's smart to do validation and sanity
351 # checks on this data in case a user "hacks" the input
352 # Add constraints to the field
354 # Make sure it's a number
362 # Global filters and constraints.
364 # The user cannot leave any fields blank
366 # If not all fields are required, move the Required constraint to the
369 # Remove whitespace at both ends
371 # Escape HTML characters for safety
374 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
375 tricky. See the L<Config::General Config for this tutorial> section of
376 this document for a more foolproof config format.
378 The main changes are:
384 The C<Select> element for C<authors> is changed from a single-select
385 drop-down to a multi-select list by adding configuration for the
386 C<multiple> and C<size> options in C<formfu_create.yml>.
390 Constraints are added to provide validation of the user input. See
391 L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other
392 constraints that are available.
396 A variety of filters are run on every field to remove and escape
397 unwanted input. See L<HTML::FormFu::Filter|HTML::FormFu::Filter>
398 for more filter options.
403 =head2 Try Out the Updated Form
405 Press C<Ctrl-C> to kill the previous server instance (if it's still
406 running) and restart it:
408 $ script/myapp_server.pl
410 Make sure you are still logged in as C<test01> and try adding a book
411 with various errors: title less than 5 characters, non-numeric rating, a
412 rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
413 When you click Submit, the HTML::FormFu C<constraint> items will
414 validate the logic and insert feedback as appropriate. Try adding blank
415 spaces at the front or the back of the title and note that it will be
419 =head1 CREATE AND UPDATE/EDIT ACTION
421 Let's expand the work done above to add an edit action. First, open
422 C<lib/MyApp/Controller/Books.pm> and add the following method to the
427 Use HTML::FormFu to update an existing book
431 sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0)
432 :FormConfig('books/formfu_create.yml') {
435 # Get the specified book already saved by the 'object' method
436 my $book = $c->stash->{object};
438 # Make sure we were able to get a book
440 $c->flash->{error_msg} = "Invalid book -- Cannot edit";
441 $c->response->redirect($c->uri_for($self->action_for('list')));
445 # Get the form that the :FormConfig attribute saved in the stash
446 my $form = $c->stash->{form};
448 # Check if the form has been submitted (vs. displaying the initial
449 # form) and if the data passed validation. "submitted_and_valid"
450 # is shorthand for "$form->submitted && !$form->has_errors"
451 if ($form->submitted_and_valid) {
452 # Save the form data for the book
453 $form->model->update($book);
454 # Set a status message for the user
455 $c->flash->{status_msg} = 'Book edited';
456 # Return to the books list
457 $c->response->redirect($c->uri_for($self->action_for('list')));
460 # Get the authors from the DB
461 my @author_objs = $c->model("DB::Author")->all();
462 # Create an array of arrayrefs where each arrayref is an author
464 foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
465 push(@authors, [$_->id, $_->last_name]);
467 # Get the select added by the config file
468 my $select = $form->get_element({type => 'Select'});
469 # Add the authors to it
470 $select->options(\@authors);
471 # Populate the form with existing values from DB
472 $form->model->default_values($book);
476 $c->stash->{template} = 'books/formfu_create.tt2';
479 Most of this code should look familiar to what we used in the
480 C<formfu_create> method (in fact, we should probably centralize some of
481 the common code in separate methods). The main differences are:
487 We have to manually specify the name of the FormFu .yml file as an
488 argument to C<:FormConfig> because the name can no longer be
489 automatically deduced from the name of our action/method (by default,
490 FormFu would look for a file named C<books/formfu_edit.yml>).
494 We load the book object from the stash (found using the $id passed to
495 the Chained object method)
499 We use C<$id> to look up the existing book from the database.
503 We make sure the book lookup returned a valid book. If not, we set
504 the error message and return to the book list.
508 If the form has been submitted and passes validation, we skip creating a
509 new book and just use C<$form-E<gt>model-E<gt>update> to update the existing
514 If the form is being displayed for the first time (or has failed
515 validation and it being redisplayed), we use
516 C<$form-E<gt>model-E<gt>default_values> to populate the form with data from the
521 Then, edit C<root/src/books/list.tt2> and add a new link below the
522 existing "Delete" link that allows us to edit/update each existing book.
523 The last E<lt>tdE<gt> cell in the book list table should look like the
528 [% # Add a link to delete a book %]
529 <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
530 [% # Add a link to edit a book %]
531 <a href="[% c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
535 B<Note:> Only add two lines (the "Add a link to edit a book" comment
536 and the href for C<formfu_edit>). Make sure you add it below the
537 existing C<delete> link.
540 =head2 Try Out the Edit/Update Feature
542 Press C<Ctrl-C> to kill the previous server instance (if it's still
543 running) and restart it:
545 $ script/myapp_server.pl
547 Make sure you are still logged in as C<test01> and go to the
548 L<http://localhost:3000/books/list> URL in your browser. Click the
549 "Edit" link next to "Internetworking with TCP/IP Vol. II", change the
550 rating to a 3, the "II" at end of the title to the number "2", add
551 Stevens as a co-author (control-click), and click Submit. You will then
552 be returned to the book list with a "Book edited" message at the top in
553 green. Experiment with other edits to various books.
556 =head2 More Things to Try
558 You are now armed with enough knowledge to be dangerous. You can keep
559 tweaking the example application; some things you might want to do:
565 Add an appropriate authorization check to the new Edit function.
569 Cleanup the List page so that the Login link only displays when the user
570 isn't logged in and the Logout link only displays when a user is logged
575 Add a more sensible policy for when and how users and admins can do
576 things in the CRUD cycle.
580 Support the CRUD cycle for authors.
584 Or you can proceed to write your own application, which is probably the
585 real reason you worked through this Tutorial in the first place.
588 =head2 Config::General Config for this tutorial
590 If you are having difficulty with YAML config above, please save the
591 below into the file C<formfu_create.conf> and delete the
592 C<formfu_create.yml> file. The below is in
593 L<Config::General|Config::General> format which follows the syntax of
602 message Length must be between 5 and 40 characters
610 title Enter a book title here
621 title Enter a rating between 1 and 5 here
644 Kennedy Clark, C<hkclark@gmail.com>
646 Please report any errors, issues or suggestions to the author. The
647 most recent version of the Catalyst Tutorial can be found at
648 L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
650 Copyright 2006-2008, Kennedy Clark, under Creative Commons License
651 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).