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<extends> line from the default of:
82 BEGIN {extends 'Catalyst::Controller'; }
84 to use the FormFu base controller class:
86 BEGIN {extends 'Catalyst::Controller::HTML::FormFu'; }
90 requires 'Catalyst::Controller::HTML::FormFu';
92 to your C<Makefile.PL>.
94 =head2 Add Action to Display and Save the Form
96 Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
101 Use HTML::FormFu to create a new book
105 sub formfu_create :Chained('base') :PathPart('formfu_create') :Args(0) :FormConfig {
108 # Get the form that the :FormConfig attribute saved in the stash
109 my $form = $c->stash->{form};
111 # Check if the form has been submitted (vs. displaying the initial
112 # form) and if the data passed validation. "submitted_and_valid"
113 # is shorthand for "$form->submitted && !$form->has_errors"
114 if ($form->submitted_and_valid) {
116 my $book = $c->model('DB::Book')->new_result({});
117 # Save the form data for the book
118 $form->model->update($book);
119 # Set a status message for the user
120 $c->flash->{status_msg} = 'Book created';
121 # Return to the books list
122 $c->response->redirect($c->uri_for($self->action_for('list')));
125 # Get the authors from the DB
126 my @author_objs = $c->model("DB::Author")->all();
127 # Create an array of arrayrefs where each arrayref is an author
129 foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
130 push(@authors, [$_->id, $_->last_name]);
132 # Get the select added by the config file
133 my $select = $form->get_element({type => 'Select'});
134 # Add the authors to it
135 $select->options(\@authors);
139 $c->stash->{template} = 'books/formfu_create.tt2';
143 =head2 Create a Form Config File
145 Although C<HTML::FormFu> supports any configuration file handled by
146 L<Config::Any|Config::Any>, most people tend to use YAML. First
147 create a directory to hold your form configuration files:
149 mkdir -p root/forms/books
151 Then create the file C<root/forms/books/formfu_create.yml> and enter the
155 # indicator is the field that is used to test for form submission
157 # Start listing the form elements
159 # The first element will be a text field for the title
163 # This is an optional 'mouse over' title pop-up
165 title: Enter a book title here
167 # Another text field for the numeric rating
172 title: Enter a rating between 1 and 5 here
174 # Add a drop-down list for the author selection. Note that we will
175 # dynamically fill in all the authors from the controller but we
176 # could manually set items in the drop-list by adding this YAML code:
178 # - [ '1', 'Bastien' ]
179 # - [ '2', 'Nasseh' ]
189 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
190 tricky. See the L<Config::General Config for this tutorial> section of
191 this document for a more foolproof config format.
194 =head2 Update the CSS
196 Edit C<root/static/css/main.css> and add the following lines to the bottom of
211 These changes will display form elements vertically. Note that the
212 existing definition of the C<.error> class is pulling the color scheme
213 settings from the C<root/lib/config/col> file that was created by the
214 TTSite helper. This allows control over the CSS color settings from a
218 =head2 Create a Template Page To Display The Form
220 Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
222 [% META title = 'Create/Update Book' %]
224 [%# Render the HTML::FormFu Form %]
227 <p><a href="[% c.uri_for(c.controller.action_for('list')) %]">Return to book list</a></p>
230 =head2 Add Links for Create and Update via C<HTML::FormFu>
232 Open C<root/src/books/list.tt2> in your editor and add the following to
233 the bottom of the existing file:
238 <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a>
241 This adds a new link to the bottom of the book list page that we can
242 use to easily launch our HTML::FormFu-based form.
245 =head2 Test The HTML::FormFu Create Form
247 Press C<Ctrl-C> to kill the previous server instance (if it's still
248 running) and restart it:
250 $ script/myapp_server.pl
252 Login as C<test01> (password: mypass). Once at the Book List page,
253 click the new HTML::FormFu "Create" link at the bottom to display the
254 form. Fill in the following values:
256 Title: Internetworking with TCP/IP Vol. II
260 Click the Submit button, and you will be returned to the Book List page
261 with a "Book created" status message displayed.
263 Also note that this implementation allows you to create books with any
264 bogus information. Although we have constrained the authors with the
265 drop-down list (note that this isn't bulletproof because we still have
266 not prevented a user from "hacking" the form to specify other values),
267 there are no restrictions on items such as the length of the title (for
268 example, you can create a one-letter title) and the value of the rating
269 (you can use any number you want, and even non-numeric values with
270 SQLite). The next section will address this concern.
272 B<Note:> Depending on the database you are using and how you established
273 the columns in your tables, the database could obviously provide various
274 levels of "type enforcement" on your data. The key point being made in
275 the previous paragraph is that the I<web application> itself is not
276 performing any validation.
279 =head1 HTML::FormFu VALIDATION AND FILTERING
281 Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section
282 did provide an automated mechanism to build the form, the real power of
283 this module stems from functionality that can automatically validate and
284 filter the user input. Validation uses constraints to be sure that
285 users input appropriate data (for example, that the email field of a
286 form contains a valid email address). Filtering can also be used to
287 remove extraneous whitespace from fields or to escape meta-characters in
291 =head2 Add Constraints
293 Open C<root/forms/books/formfu_create.yml> in your editor and update it
297 # indicator is the field that is used to test for form submission
299 # Start listing the form elements
301 # The first element will be a text field for the title
305 # This is an optional 'mouse over' title pop-up
307 title: Enter a book title here
308 # Add constraints for the field
310 # Force the length to be between 5 and 40 chars
314 # Override the default of 'Invalid input'
315 message: Length must be between 5 and 40 characters
317 # Another text field for the numeric rating
322 title: Enter a rating between 1 and 5 here
323 # Use Filter to clean up the input data
324 # Could use 'NonNumeric' below, but since Filters apply *before*
325 # constraints, it would conflict with the 'Integer' constraint below.
326 # So let's skip this and just use the constraint.
328 # Remove everything except digits
330 # Add constraints to the field
332 # Make sure it's a number
334 message: "Required. Digits only, please."
335 # Check the min & max values
339 message: "Must be between 1 and 5."
341 # Add a select list for the author selection. Note that we will
342 # dynamically fill in all the authors from the controller but we
343 # could manually set items in the select by adding this YAML code:
345 # - [ '1', 'Bastien' ]
346 # - [ '2', 'Nasseh' ]
350 # Convert the drop-down to a multi-select list
352 # Display 3 entries (user can scroll to see others)
354 # One could argue we don't need to do filters or constraints for
355 # a select list, but it's smart to do validation and sanity
356 # checks on this data in case a user "hacks" the input
357 # Add constraints to the field
359 # Make sure it's a number
367 # Global filters and constraints.
369 # The user cannot leave any fields blank
371 # If not all fields are required, move the Required constraint to the
374 # Remove whitespace at both ends
376 # Escape HTML characters for safety
379 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
380 tricky. See the L<Config::General Config for this tutorial> section of
381 this document for a more foolproof config format.
383 The main changes are:
389 The C<Select> element for C<authors> is changed from a single-select
390 drop-down to a multi-select list by adding configuration for the
391 C<multiple> and C<size> options in C<formfu_create.yml>.
395 Constraints are added to provide validation of the user input. See
396 L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other
397 constraints that are available.
401 A variety of filters are run on every field to remove and escape
402 unwanted input. See L<HTML::FormFu::Filter|HTML::FormFu::Filter>
403 for more filter options.
408 =head2 Try Out the Updated Form
410 Press C<Ctrl-C> to kill the previous server instance (if it's still
411 running) and restart it:
413 $ script/myapp_server.pl
415 Make sure you are still logged in as C<test01> and try adding a book
416 with various errors: title less than 5 characters, non-numeric rating, a
417 rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
418 When you click Submit, the HTML::FormFu C<constraint> items will
419 validate the logic and insert feedback as appropriate. Try adding blank
420 spaces at the front or the back of the title and note that it will be
424 =head1 CREATE AND UPDATE/EDIT ACTION
426 Let's expand the work done above to add an edit action. First, open
427 C<lib/MyApp/Controller/Books.pm> and add the following method to the
432 Use HTML::FormFu to update an existing book
436 sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0)
437 :FormConfig('books/formfu_create.yml') {
440 # Get the specified book already saved by the 'object' method
441 my $book = $c->stash->{object};
443 # Make sure we were able to get a book
445 $c->flash->{error_msg} = "Invalid book -- Cannot edit";
446 $c->response->redirect($c->uri_for($self->action_for('list')));
450 # Get the form that the :FormConfig attribute saved in the stash
451 my $form = $c->stash->{form};
453 # Check if the form has been submitted (vs. displaying the initial
454 # form) and if the data passed validation. "submitted_and_valid"
455 # is shorthand for "$form->submitted && !$form->has_errors"
456 if ($form->submitted_and_valid) {
457 # Save the form data for the book
458 $form->model->update($book);
459 # Set a status message for the user
460 $c->flash->{status_msg} = 'Book edited';
461 # Return to the books list
462 $c->response->redirect($c->uri_for($self->action_for('list')));
465 # Get the authors from the DB
466 my @author_objs = $c->model("DB::Author")->all();
467 # Create an array of arrayrefs where each arrayref is an author
469 foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
470 push(@authors, [$_->id, $_->last_name]);
472 # Get the select added by the config file
473 my $select = $form->get_element({type => 'Select'});
474 # Add the authors to it
475 $select->options(\@authors);
476 # Populate the form with existing values from DB
477 $form->model->default_values($book);
481 $c->stash->{template} = 'books/formfu_create.tt2';
484 Most of this code should look familiar to what we used in the
485 C<formfu_create> method (in fact, we should probably centralize some of
486 the common code in separate methods). The main differences are:
492 We have to manually specify the name of the FormFu .yml file as an
493 argument to C<:FormConfig> because the name can no longer be
494 automatically deduced from the name of our action/method (by default,
495 FormFu would look for a file named C<books/formfu_edit.yml>).
499 We load the book object from the stash (found using the $id passed to
500 the Chained object method)
504 We use C<$id> to look up the existing book from the database.
508 We make sure the book lookup returned a valid book. If not, we set
509 the error message and return to the book list.
513 If the form has been submitted and passes validation, we skip creating a
514 new book and just use C<$form-E<gt>model-E<gt>update> to update the existing
519 If the form is being displayed for the first time (or has failed
520 validation and it being redisplayed), we use
521 C<$form-E<gt>model-E<gt>default_values> to populate the form with data from the
526 Then, edit C<root/src/books/list.tt2> and add a new link below the
527 existing "Delete" link that allows us to edit/update each existing book.
528 The last E<lt>tdE<gt> cell in the book list table should look like the
533 [% # Add a link to delete a book %]
534 <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
535 [% # Add a link to edit a book %]
536 <a href="[% c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
540 B<Note:> Only add two lines (the "Add a link to edit a book" comment
541 and the href for C<formfu_edit>). Make sure you add it below the
542 existing C<delete> link.
545 =head2 Try Out the Edit/Update Feature
547 Press C<Ctrl-C> to kill the previous server instance (if it's still
548 running) and restart it:
550 $ script/myapp_server.pl
552 Make sure you are still logged in as C<test01> and go to the
553 L<http://localhost:3000/books/list> URL in your browser. Click the
554 "Edit" link next to "Internetworking with TCP/IP Vol. II", change the
555 rating to a 3, the "II" at end of the title to the number "2", add
556 Stevens as a co-author (control-click), and click Submit. You will then
557 be returned to the book list with a "Book edited" message at the top in
558 green. Experiment with other edits to various books.
561 =head2 More Things to Try
563 You are now armed with enough knowledge to be dangerous. You can keep
564 tweaking the example application; some things you might want to do:
570 Add an appropriate authorization check to the new Edit function.
574 Cleanup the List page so that the Login link only displays when the user
575 isn't logged in and the Logout link only displays when a user is logged
580 Add a more sensible policy for when and how users and admins can do
581 things in the CRUD cycle.
585 Support the CRUD cycle for authors.
589 Or you can proceed to write your own application, which is probably the
590 real reason you worked through this Tutorial in the first place.
593 =head2 Config::General Config for this tutorial
595 If you are having difficulty with YAML config above, please save the
596 below into the file C<formfu_create.conf> and delete the
597 C<formfu_create.yml> file. The below is in
598 L<Config::General|Config::General> format which follows the syntax of
607 message Length must be between 5 and 40 characters
615 title Enter a book title here
626 title Enter a rating between 1 and 5 here
649 Kennedy Clark, C<hkclark@gmail.com>
651 Please report any errors, issues or suggestions to the author. The
652 most recent version of the Catalyst Tutorial can be found at
653 L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
655 Copyright 2006-2008, Kennedy Clark, under Creative Commons License
656 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).