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> 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>
66 for additional form management options other than
70 =head1 HTML::FormFu FORM CREATION
72 This section looks at how L<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>
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>, 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.
214 =head2 Create a Template Page To Display The Form
216 Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
218 [% META title = 'Create/Update Book' %]
220 [%# Render the HTML::FormFu Form %]
223 <p><a href="[% c.uri_for(c.controller.action_for('list'))
224 %]">Return to book list</a></p>
227 =head2 Add Links for Create and Update via C<HTML::FormFu>
229 Open C<root/src/books/list.tt2> in your editor and add the following to
230 the bottom of the existing file:
235 <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a>
238 This adds a new link to the bottom of the book list page that we can
239 use to easily launch our HTML::FormFu-based form.
242 =head2 Test The HTML::FormFu Create Form
244 Make sure the server is running with the "-r" restart option:
246 $ script/myapp_server.pl -r
248 Login as C<test01> (password: mypass). Once at the Book List page,
249 click the new HTML::FormFu "Create" link at the bottom to display the
250 form. Fill in the following values:
252 Title: Internetworking with TCP/IP Vol. II
256 Click the Submit button, and you will be returned to the Book List page
257 with a "Book created" status message displayed.
259 Also note that this implementation allows you to create books with any
260 bogus information. Although we have constrained the authors with the
261 drop-down list (note that this isn't bulletproof because we still have
262 not prevented a user from "hacking" the form to specify other values),
263 there are no restrictions on items such as the length of the title (for
264 example, you can create a one-letter title) and the value of the rating
265 (you can use any number you want, and even non-numeric values with
266 SQLite). The next section will address this concern.
268 B<Note:> Depending on the database you are using and how you established
269 the columns in your tables, the database could obviously provide various
270 levels of "type enforcement" on your data. The key point being made in
271 the previous paragraph is that the I<web application> itself is not
272 performing any validation.
275 =head1 HTML::FormFu VALIDATION AND FILTERING
277 Although the use of L<HTML::FormFu> in the previous section
278 did provide an automated mechanism to build the form, the real power of
279 this module stems from functionality that can automatically validate and
280 filter the user input. Validation uses constraints to be sure that
281 users input appropriate data (for example, that the email field of a
282 form contains a valid email address). Filtering can also be used to
283 remove extraneous whitespace from fields or to escape meta-characters in
287 =head2 Add Constraints
289 Open C<root/forms/books/formfu_create.yml> in your editor and update it
293 # indicator is the field that is used to test for form submission
295 # Start listing the form elements
297 # The first element will be a text field for the title
301 # This is an optional 'mouse over' title pop-up
303 title: Enter a book title here
304 # Add constraints for the field
306 # Force the length to be between 5 and 40 chars
310 # Override the default of 'Invalid input'
311 message: Length must be between 5 and 40 characters
313 # Another text field for the numeric rating
318 title: Enter a rating between 1 and 5 here
319 # Use Filter to clean up the input data
320 # Could use 'NonNumeric' below, but since Filters apply *before*
321 # constraints, it would conflict with the 'Integer' constraint below.
322 # So let's skip this and just use the constraint.
324 # Remove everything except digits
326 # Add constraints to the field
328 # Make sure it's a number
330 message: "Required. Digits only, please."
331 # Check the min & max values
335 message: "Must be between 1 and 5."
337 # Add a select list for the author selection. Note that we will
338 # dynamically fill in all the authors from the controller but we
339 # could manually set items in the select by adding this YAML code:
341 # - [ '1', 'Bastien' ]
342 # - [ '2', 'Nasseh' ]
346 # Convert the drop-down to a multi-select list
348 # Display 3 entries (user can scroll to see others)
350 # One could argue we don't need to do filters or constraints for
351 # a select list, but it's smart to do validation and sanity
352 # checks on this data in case a user "hacks" the input
353 # Add constraints to the field
355 # Make sure it's a number
363 # Global filters and constraints.
365 # The user cannot leave any fields blank
367 # If not all fields are required, move the Required constraint to the
370 # Remove whitespace at both ends
372 # Escape HTML characters for safety
375 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
376 tricky. See the L<Config::General Config for this tutorial> section of
377 this document for a more foolproof config format.
379 The main changes are:
385 The C<Select> element for C<authors> is changed from a single-select
386 drop-down to a multi-select list by adding configuration for the
387 C<multiple> and C<size> options in C<formfu_create.yml>.
391 Constraints are added to provide validation of the user input. See
392 L<HTML::FormFu::Constraint> for other
393 constraints that are available.
397 A variety of filters are run on every field to remove and escape
398 unwanted input. See L<HTML::FormFu::Filter>
399 for more filter options.
404 =head2 Try Out the Updated Form
406 Make sure you are still logged in as C<test01> and try adding a book
407 with various errors: title less than 5 characters, non-numeric rating, a
408 rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
409 When you click Submit, the HTML::FormFu C<constraint> items will
410 validate the logic and insert feedback as appropriate. Try adding blank
411 spaces at the front or the back of the title and note that it will be
415 =head1 CREATE AND UPDATE/EDIT ACTION
417 Let's expand the work done above to add an edit action. First, open
418 C<lib/MyApp/Controller/Books.pm> and add the following method to the
423 Use HTML::FormFu to update an existing book
427 sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0)
428 :FormConfig('books/formfu_create.yml') {
431 # Get the specified book already saved by the 'object' method
432 my $book = $c->stash->{object};
434 # Make sure we were able to get a book
436 $c->flash->{error_msg} = "Invalid book -- Cannot edit";
437 $c->response->redirect($c->uri_for($self->action_for('list')));
441 # Get the form that the :FormConfig attribute saved in the stash
442 my $form = $c->stash->{form};
444 # Check if the form has been submitted (vs. displaying the initial
445 # form) and if the data passed validation. "submitted_and_valid"
446 # is shorthand for "$form->submitted && !$form->has_errors"
447 if ($form->submitted_and_valid) {
448 # Save the form data for the book
449 $form->model->update($book);
450 # Set a status message for the user
451 $c->flash->{status_msg} = 'Book edited';
452 # Return to the books list
453 $c->response->redirect($c->uri_for($self->action_for('list')));
456 # Get the authors from the DB
457 my @author_objs = $c->model("DB::Author")->all();
458 # Create an array of arrayrefs where each arrayref is an author
460 foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
461 push(@authors, [$_->id, $_->last_name]);
463 # Get the select added by the config file
464 my $select = $form->get_element({type => 'Select'});
465 # Add the authors to it
466 $select->options(\@authors);
467 # Populate the form with existing values from DB
468 $form->model->default_values($book);
472 $c->stash(template => 'books/formfu_create.tt2');
475 Most of this code should look familiar to what we used in the
476 C<formfu_create> method (in fact, we should probably centralize some of
477 the common code in separate methods). The main differences are:
483 We have to manually specify the name of the FormFu .yml file as an
484 argument to C<:FormConfig> because the name can no longer be
485 automatically deduced from the name of our action/method (by default,
486 FormFu would look for a file named C<books/formfu_edit.yml>).
490 We load the book object from the stash (found using the $id passed to
491 the Chained object method)
495 We use C<$id> to look up the existing book from the database.
499 We make sure the book lookup returned a valid book. If not, we set
500 the error message and return to the book list.
504 If the form has been submitted and passes validation, we skip creating a
505 new book and just use C<$form-E<gt>model-E<gt>update> to update the existing
510 If the form is being displayed for the first time (or has failed
511 validation and it being redisplayed), we use
512 C<$form-E<gt>model-E<gt>default_values> to populate the form with data from the
517 Then, edit C<root/src/books/list.tt2> and add a new link below the
518 existing "Delete" link that allows us to edit/update each existing book.
519 The last E<lt>tdE<gt> cell in the book list table should look like the
524 [% # Add a link to delete a book %]
525 <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
526 [% # Add a link to edit a book %]
527 <a href="[% c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
531 B<Note:> Only add two lines (the "Add a link to edit a book" comment
532 and the href for C<formfu_edit>). Make sure you add it below the
533 existing C<delete> link.
536 =head2 Try Out the Edit/Update Feature
538 Make sure you are still logged in as C<test01> and go to the
539 L<http://localhost:3000/books/list> URL in your browser. Click the
540 "Edit" link next to "Internetworking with TCP/IP Vol. II", change the
541 rating to a 3, the "II" at end of the title to the number "2", add
542 Stevens as a co-author (control-click), and click Submit. You will then
543 be returned to the book list with a "Book edited" message at the top in
544 green. Experiment with other edits to various books.
547 =head2 More Things to Try
549 You are now armed with enough knowledge to be dangerous. You can keep
550 tweaking the example application; some things you might want to do:
556 Add an appropriate authorization check to the new Edit function.
560 Cleanup the List page so that the Login link only displays when the user
561 isn't logged in and the Logout link only displays when a user is logged
566 Add a more sensible policy for when and how users and admins can do
567 things in the CRUD cycle.
571 Support the CRUD cycle for authors.
575 Or you can proceed to write your own application, which is probably the
576 real reason you worked through this Tutorial in the first place.
579 =head2 Config::General Config for this tutorial
581 If you are having difficulty with YAML config above, please save the
582 below into the file C<formfu_create.conf> and delete the
583 C<formfu_create.yml> file. The below is in
584 L<Config::General> format which follows the syntax of
593 message Length must be between 5 and 40 characters
601 title Enter a book title here
612 title Enter a rating between 1 and 5 here
635 Kennedy Clark, C<hkclark@gmail.com>
637 Feel free to contact the author for any errors or suggestions, but the
638 best way to report issues is via the CPAN RT Bug system at
639 <https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
641 The most recent version of the Catalyst Tutorial can be found at
642 L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
644 Copyright 2006-2010, Kennedy Clark, under the
645 Creative Commons Attribution Share-Alike License Version 3.0
646 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).