5 Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Part 9: Advanced CRUD - FormFu
10 This is B<Part 9 of 10> for the Catalyst tutorial.
12 L<Tutorial Overview|Catalyst::Manual::Tutorial>
18 L<Introduction|Catalyst::Manual::Tutorial::Intro>
22 L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
26 L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics>
30 L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
34 L<Authentication|Catalyst::Manual::Tutorial::Authentication>
38 L<Authorization|Catalyst::Manual::Tutorial::Authorization>
42 L<Debugging|Catalyst::Manual::Tutorial::Debugging>
46 L<Testing|Catalyst::Manual::Tutorial::Testing>
50 B<Advanced CRUD::FormFu>
54 L<Appendices|Catalyst::Manual::Tutorial::Appendices>
61 This portion of the tutorial explores L<HTML::FormFu|HTML::FormFu> and
62 how it can be used to manage forms, perform validation of form input,
63 as well as save and restore data to/from the database. This was written
64 using HTML::FormFu version 0.03007.
67 L<Catalyst::Manual::Tutorial::AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
68 for additional form management options other than
69 L<HTML::FormFu|HTML::FormFu>.
72 =head1 Install C<HTML::FormFu>
74 If you are following along in Ubuntu, it turns out that C<HTML::FormFu>
75 is not yet available as a package at the time this was written. To
76 install it with a combination of C<apt-get> packages and traditional
77 CPAN modules, first use C<apt-get> to install most of the modules
78 required by C<HTML::FormFu>:
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 \
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
91 Then use the following command to install directly from CPAN the modules
92 that aren't available as Ubuntu/Debian packages via C<apt-get>:
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
101 Is it OK to try to connect to the Internet? [yes] yes
106 B<Note:> If you are following along with the Ubuntu LiveCD, you might
107 want to make sure you still have adequate free disk space in the root
108 partition with the C<df> command. You can free up some space with
109 C<rm -rf /root/.cpan/*>.
112 =head1 C<HTML::FormFu> FORM CREATION
114 This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to
115 add additional functionality to the manually created form from Part 4.
118 =head2 Inherit From C<Catalyst::Controller::HTML::FormFu>
120 First, change your C<lib/MyApp/Controller/Books.pm> to inherit from
121 L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu>
122 by changing the C<use base> line from the default of:
124 use parent 'Catalyst::Controller';
126 to use the FormFu base controller class:
128 use parent 'Catalyst::Controller::HTML::FormFu';
131 =head2 Add Action to Display and Save the Form
133 Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
138 Use HTML::FormFu to create a new book
142 sub formfu_create :Local :FormConfig {
145 # Get the form that the :FormConfig attribute saved in the stash
146 my $form = $c->stash->{form};
148 # Check if the form has been submitted (vs. displaying the initial
149 # form) and if the data passed validation. "submitted_and_valid"
150 # is shorthand for "$form->submitted && !$form->has_errors"
151 if ($form->submitted_and_valid) {
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'));
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
166 foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) {
167 push(@authors, [$_->id, $_->last_name]);
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);
176 $c->stash->{template} = 'books/formfu_create.tt2';
180 =head2 Create a Form Config File
182 Although C<HTML::FormFu> supports any configuration file handled by
183 L<Config::Any|Config::Any>, most people tend to use YAML. First
184 create a directory to hold your form configuration files:
186 mkdir -p root/forms/books
188 Then create the file C<root/forms/books/formfu_create.yml> and enter the
192 # indicator is the field that is used to test for form submission
194 # Start listing the form elements
196 # The first element will be a text field for the title
200 # This is an optional 'mouse over' title pop-up
202 title: Enter a book title here
204 # Another text field for the numeric rating
209 title: Enter a rating between 1 and 5 here
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:
215 # - [ '1', 'Bastien' ]
216 # - [ '2', 'Nasseh' ]
226 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
227 tricky. See the L<Config::General Config for this tutorial> section of
228 this document for a more foolproof config format.
231 =head2 Update the CSS
233 Edit C<root/static/css/main.css> and add the following lines to the bottom of
247 These changes will display form elements vertically. Note that the
248 existing definition of the C<.error> class is pulling the color scheme
249 settings from the C<root/lib/config/col> file that was created by the
250 TTSite helper. This allows control over the CSS color settings from a
254 =head2 Create a Template Page To Display The Form
256 Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
258 [% META title = 'Create/Update Book' %]
260 [%# Render the HTML::FormFu Form %]
263 <p><a href="[% c.uri_for('list') %]">Return to book list</a></p>
266 =head2 Add Links for Create and Update via C<HTML::FormFu>
268 Open C<root/src/books/list.tt2> in your editor and add the following to
269 the bottom of the existing file:
273 <a href="[% c.uri_for('formfu_create') %]">Create</a>
276 This adds a new link to the bottom of the book list page that we can
277 use to easily launch our HTML::FormFu-based form.
280 =head2 Test The <HTML::FormFu> Create Form
282 Press C<Ctrl-C> to kill the previous server instance (if it's still
283 running) and restart it:
285 $ script/myapp_server.pl
287 Login as C<test01> (password: mypass). Once at the Book List page,
288 click the new HTML::FormFu "Create" link at the bottom to display the
289 form. Fill in the following values: Title = "Internetworking with
290 TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit,
291 and you will be returned to the Book List page with a "Book created"
292 status message displayed.
294 Also note that this implementation allows you to can create books with
295 bogus information. Although we have constrained the authors with the
296 drop-down list (note that this isn't bulletproof because we still have
297 not prevented a user from "hacking" the form to specify other values),
298 there are no restrictions on items such as the length of the title (for
299 example, 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
301 SQLite). The next section will address this concern.
303 B<Note:> Depending on the database you are using and how you established
304 the columns in your tables, the database could obviously provide various
305 levels of "type enforcement" on your data. The key point being made in
306 the previous paragraph is that the I<web application> itself is not
307 performing any validation.
310 =head1 C<HTML::FormFu> VALIDATION AND FILTERING
312 Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section
313 did provide an automated mechanism to build the form, the real power of
314 this module stems from functionality that can automatically validate and
315 filter the user input. Validation uses constraints to be sure that
316 users input appropriate data (for example, that the email field of a
317 form contains a valid email address). Filtering can also be used to
318 remove extraneous whitespace from fields or to escape meta-characters in
322 =head2 Add Constraints
324 Open C<root/forms/books/formfu_create.yml> in your editor and update it
328 # indicator is the field that is used to test for form submission
330 # Start listing the form elements
332 # The first element will be a text field for the title
336 # This is an optional 'mouse over' title pop-up
338 title: Enter a book title here
339 # Add constraints for the field
341 # Force the length to be between 5 and 40 chars
345 # Override the default of 'Invalid input'
346 message: Length must be between 5 and 40 characters
348 # Another text field for the numeric rating
353 title: Enter a rating between 1 and 5 here
354 # Use Filter to clean up the input data
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.
359 # Remove everything except digits
361 # Add constraints to the field
363 # Make sure it's a number
365 message: "Required. Digits only, please."
366 # Check the min & max values
370 message: "Must be between 1 and 5."
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:
376 # - [ '1', 'Bastien' ]
377 # - [ '2', 'Nasseh' ]
381 # Convert the drop-down to a multi-select list
383 # Display 3 entries (user can scroll to see others)
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
388 # Add constraints to the field
390 # Make sure it's a number
398 # Global filters and constraints.
400 # The user cannot leave any fields blank
402 # If not all fields are required, move the Required constraint to the
405 # Remove whitespace at both ends
407 # Escape HTML characters for safety
410 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
411 tricky. See the L<Config::General Config for this tutorial> section of
412 this document for a more foolproof config format.
414 The main changes are:
420 The C<Select> element for C<authors> is changed from a single-select
421 drop-down to a multi-select list by adding configuration for the
422 C<multiple> and C<size> options in C<formfu_create.yml>.
426 Constraints are added to provide validation of the user input. See
427 L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other
428 constraints that are available.
432 A variety of filters are run on every field to remove and escape
433 unwanted input. See L<HTML::FormFu::Filter|HTML::FormFu::Filter>
434 for more filter options.
439 =head2 Try Out the Updated Form
441 Press C<Ctrl-C> to kill the previous server instance (if it's still
442 running) and restart it:
444 $ script/myapp_server.pl
446 Make sure you are still logged in as C<test01> and try adding a book
447 with various errors: title less than 5 characters, non-numeric rating, a
448 rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
449 When you click Submit, the HTML::FormFu C<constraint> items will
450 validate the logic and insert feedback as appropriate. Try adding blank
451 spaces at the front or the back of the title and note that it will be
455 =head1 CREATE AND UPDATE/EDIT ACTION
457 Let's expand the work done above to add an edit action. First, open
458 C<lib/MyApp/Controller/Books.pm> and add the following method to the
463 Use HTML::FormFu to update an existing book
467 sub formfu_edit :Local :FormConfig('books/formfu_create.yml') {
468 my ($self, $c, $id) = @_;
470 # Get the specified book
471 my $book = $c->model('DB::Books')->find($id);
473 # Make sure we were able to get a book
475 $c->flash->{error_msg} = "Invalid book -- Cannot edit";
476 $c->response->redirect($c->uri_for('list'));
480 # Get the form that the :FormConfig attribute saved in the stash
481 my $form = $c->stash->{form};
483 # Check if the form has been submitted (vs. displaying the initial
484 # form) and if the data passed validation. "submitted_and_valid"
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'));
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
499 foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) {
500 push(@authors, [$_->id, $_->last_name]);
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);
511 $c->stash->{template} = 'books/formfu_create.tt2';
514 Most of this code should look familiar to what we used in the
515 C<formfu_create> method (in fact, we should probably centralize some of
516 the common code in separate methods). The main differences are:
522 We accept C<$id> as an argument via the URL.
526 We use C<$id> to look up the existing book from the database.
530 We make sure the C<$id> and book lookup returned a valid book. If not,
531 we set the error message and return to the book list.
535 If the form has been submitted and passes validation, we skip creating a
536 new book and just use C<$form-E<gt>save_to_model> to update the existing
541 If the form is being displayed for the first time (or has failed
542 validation and it being redisplayed), we use
543 C<$form-E<gt>default_from_model> to populate the form with data from the
548 Then, edit C<root/src/books/list.tt2> and add a new link below the
549 existing "Delete" link that allows us to edit/update each existing book.
550 The last E<lt>tdE<gt> cell in the book list table should look like the
555 [% # Add a link to delete a book %]
556 <a href="[% c.uri_for('delete', book.id) %]">Delete</a>
557 [% # Add a link to edit a book %]
558 <a href="[% c.uri_for('formfu_edit', book.id) %]">Edit</a>
562 B<Note:> Only add two lines (the "Add a link to edit a book" comment
563 and the href for C<formfu_edit>). Make sure you add it below the
564 existing C<delete> link.
567 =head2 Try Out the Edit/Update Feature
569 Press C<Ctrl-C> to kill the previous server instance (if it's still
570 running) and restart it:
572 $ script/myapp_server.pl
574 Make sure you are still logged in as C<test01> and go to the
575 L<http://localhost:3000/books/list> URL in your browser. Click the
576 "Edit" link next to "Internetworking with TCP/IP Vol. II", change the
577 rating to a 3, the "II" at end of the title to the number "2", add
578 Stevens as a co-author (control-click), and click Submit. You will then
579 be returned to the book list with a "Book edited" message at the top in
580 green. Experiment with other edits to various books.
582 =head2 More Things to Try
584 You are now armed with enough knowledge to be dangerous. You can keep
585 tweaking the example application; some things you might want to do:
591 Add an appropriate ACL to the new Edit function.
595 Cleanup the List page so that the Login link only displays when the user
596 isn't logged in and the Logout link only displays when a user is logged
601 Add a more sensible policy for when and how users and admins can do
602 things in the CRUD cycle.
606 Support the CRUD cycle for authors.
610 Or you can proceed to write your own application, which is probably the
611 real reason you worked through this Tutorial in the first place.
613 =head2 Config::General Config for this tutorial
615 If you are having difficulty with YAML config above, please save the
616 below into the file C<formfu_create.conf> and delete the
617 C<formfu_create.yml> file. The below is in
618 L<Config::General|Config::General> format which follows the syntax of
627 message Length must be between 5 and 40 characters
635 title Enter a book title here
646 title Enter a rating between 1 and 5 here
670 Kennedy Clark, C<hkclark@gmail.com>
672 Please report any errors, issues or suggestions to the author. The
673 most recent version of the Catalyst Tutorial can be found at
674 L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>.
676 Copyright 2006-2008, Kennedy Clark, under Creative Commons License
677 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).