3 Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Part 9: Advanced CRUD - FormFu
6 NOTE: This part of the tutorial is in progress and will be ready soon.
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.
66 L<Catalyst::Manual::Tutorial::AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
67 for additional form management options other than
68 L<HTML::FormFu|HTML::FormFu>.
71 =head1 Install C<HTML::FormFu>
73 If you are following along in Ubuntu, it turns out that C<HTML::FormFu>
74 is not yet available as a package at the time this was written. To
75 install it with a combination of C<apt-get> packages and traditional
76 CPAN modules, first use C<apt-get> to install most of the modules
77 required by C<HTML::FormFu>:
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
84 Then use the following command to install directly from CPAN the modules
85 that aren't available as Ubuntu/Debian packages via C<apt-get>:
87 sudo cpan File::ShareDir Task::Weaken Config::Any HTML::FormFu \
88 Catalyst::Controller::HTML::FormFu
91 =head1 C<HTML::FormFu> FORM CREATION
93 This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to
94 add additional functionality to the manually created form from Part 4.
97 =head2 Inherit From C<Catalyst::Controller::HTML::FormFu>
99 First, change your C<lib/MyApp/Controller/Books.pm> to inherit from
100 L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu>
101 by changing the C<use base> line from the default of:
103 use base 'Catalyst::Controller';
105 to use the FormFu base controller class:
107 use base 'Catalyst::Controller::HTML::FormFu';
110 =head2 Add Action to Display and Save the Form
112 Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
117 Use HTML::FormFu to create a new book
121 sub formfu_create :Local :FormConfig {
124 # Get the form that the :FormConfig attribute saved in the stash
125 my $form = $c->stash->{form};
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) {
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'));
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
145 foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) {
146 push(@authors, [$_->id, $_->last_name]);
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);
155 $c->stash->{template} = 'books/formfu_create.tt2';
159 =head2 Create a Form Config File
161 Although C<HTML::FormFu> supports any configuration file handled by
162 L<Config::Any|Config::Any>, most people tend to use YAML. First
163 create a directory to hold your form configuration files:
165 mkdir -p root/forms/books
167 Then create the file C<root/forms/books/formfu_create.yml> and enter the
171 # indicator is the field that is used to test for form submission
173 # Start listing the form elements
175 # The first element will be a text field for the title
179 # This is an optional 'mouse over' title pop-up
181 title: Enter a book title here
183 # Another text field for the numeric rating
188 title: Enter a rating between 1 and 5 here
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:
194 # - [ '1', 'Bastien' ]
195 # - [ '2', 'Nasseh' ]
205 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
206 tricky. See the L<Config::General Config for this tutorial> section of
207 this document for a more foolproof config format.
210 =head2 Update the CSS
212 Edit C<root/src/ttsite.css> and add the following lines to the bottom of
226 These changes will display form elements vertically. Note that the
227 existing definition of the C<.error> class is pulling the color scheme
228 settings from the C<root/lib/config/col> file that was created by the
229 TTSite helper. This allows control over the CSS color settings from a
233 =head2 Create a Template Page To Display The Form
235 Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
237 [% META title = 'Create/Update Book' %]
239 [%# Render the HTML::FormFu Form %]
242 <p><a href="[% Catalyst.uri_for('list') %]">Return to book list</a></p>
245 =head2 Add Links for Create and Update via C<HTML::FormFu>
247 Open C<root/src/books/list.tt2> in your editor and add the following to
248 the bottom of the existing file:
252 <a href="[% Catalyst.uri_for('formfu_create') %]">Create</a>
255 This adds a new link to the bottom of the book list page that we can
256 use to easily launch our HTML::FormFu-based form.
259 =head2 Test The <HTML::FormFu> Create Form
261 Press C<Ctrl-C> to kill the previous server instance (if it's still
262 running) and restart it:
264 $ script/myapp_server.pl
266 Login as C<test01> (password: mypass). Once at the Book List page,
267 click the new HTML::FormFu "Create" link at the bottom to display the
268 form. Fill in the following values: Title = "Internetworking with
269 TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit,
270 and you will be returned to the Book List page with a "Book created"
271 status message displayed.
273 Also note that this implementation allows you to can create books with
274 bogus information. Although we have constrained the authors with the
275 drop-down list (note that this isn't bulletproof because we still have
276 not prevented a user from "hacking" the form to specify other values),
277 there are no restrictions on items such as the length of the title (for
278 example, 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
280 SQLite). The next section will address this concern.
282 B<Note:> Depending on the database you are using and how you established
283 the columns in your tables, the database could obviously provide various
284 levels of "type enforcement" on your data. The key point being made in
285 the previous paragraph is that the I<web application> itself is not
286 performing any validation.
289 =head1 C<HTML::FormFu> VALIDATION AND FILTERING
291 Although the use of L<HTML::FormFu|HTML::FormFu> in the previous section
292 did provide an automated mechanism to build the form, the real power of
293 this module stems from functionality that can automatically validate and
294 filter the user input. Validation uses constraints to be sure that
295 users input appropriate data (for example, that the email field of a
296 form contains a valid email address). Filtering can also be used to
297 remove extraneous whitespace from fields or to escape meta-characters in
301 =head2 Add Constraints
303 Open C<root/forms/books/formfu_create.yml> in your editor and update it
307 # indicator is the field that is used to test for form submission
309 # Start listing the form elements
311 # The first element will be a text field for the title
315 # This is an optional 'mouse over' title pop-up
317 title: Enter a book title here
318 # Use Filter to clean up the input data
320 # Remove whitespace at both ends
322 # Escape HTML characters for safety
324 # Add constraints for the field
326 # The user cannot leave this field blank
328 # Force the length to be between 5 and 40 chars
332 # Override the default of 'Invalid input'
333 message: Length must be between 5 and 40 characters
335 # Another text field for the numeric rating
340 title: Enter a rating between 1 and 5 here
341 # Use Filter to clean up the input data
343 # Remove whitespace at both ends
345 # Remove everything except digits
347 # Add constraints to the field
350 # Make sure it's a number
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:
357 # - [ '1', 'Bastien' ]
358 # - [ '2', 'Nasseh' ]
362 # Convert the drop-down to a multi-select list
364 # Display 3 entries (user can scroll to see others)
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
371 # Remove whitespace at both ends
373 # Escape HTML characters for safety
375 # Add constraints to the field
377 # Make sure it's a number
385 # Globally ensure that each field only specified one value
387 # The user cannot leave any fields blank
390 B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
391 tricky. See the L<Config::General Config for this tutorial> section of
392 this document for a more foolproof config format.
394 The main changes are:
400 The C<Select> element for C<authors> is changed from a single-select
401 drop-down to a multi-select list by adding configuration for the
402 C<multiple> and C<size> options in C<formfu_create.yml>.
406 Constraints are added to provide validation of the user input. See
407 L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other
408 constraints that are available.
412 A variety of filters are run on every field to remove and escape
413 unwanted input. See L<HTML::FormFu::Filter|HTML::FormFu::Filter>
414 for more filter options.
419 =head2 Try Out the Updated Form
421 Press C<Ctrl-C> to kill the previous server instance (if it's still
422 running) and restart it:
424 $ script/myapp_server.pl
426 Make sure you are still logged in as C<test01> and try adding a book
427 with various errors: title less than 5 characters, non-numeric rating, a
428 rating of 0 or 6, etc. Also try selecting one, two, and zero authors.
429 When you click Submit, the HTML::FormFu C<constraint> items will
430 validate the logic and insert feedback as appropriate. Try adding blank
431 spaces at the front or the back of the title and note that it will be
435 =head1 CREATE AND UPDATE/EDIT ACTION
437 Let's expand the work done above to add an edit action. First, open
438 C<lib/MyApp/Controller/Books.pm> and add the following method to the
443 Use HTML::FormFu to update an existing book
447 sub formfu_edit :Local :FormConfig('books/formfu_create.yml') {
448 my ($self, $c, $id) = @_;
450 # Get the specified book
451 my $book = $c->model('DB::Books')->find($id);
453 # Make sure we were able to get a book
455 $c->flash->{error_msg} = "Invalid book -- Cannot edit";
456 $c->response->redirect($c->uri_for('list'));
460 # Get the form that the :FormConfig attribute saved in the stash
461 my $form = $c->stash->{form};
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'));
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
479 foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) {
480 push(@authors, [$_->id, $_->last_name]);
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);
491 $c->stash->{template} = 'books/formfu_create.tt2';
494 Most of this code should look familiar to what we used in the
495 C<formfu_create> method (in fact, we should probably centralize some of
496 the common code in separate methods). The main differences are:
502 We accept C<$id> as an argument via the URL.
506 We use C<$id> to look up the existing book from the database.
510 We make sure the C<$id> and book lookup returned a valid book. If not,
511 we set the error message and return to the book list.
515 If the form has been submitted and passes validation, we skip creating a
516 new book and just use C<$form-E<gt>save_to_model> to update the existing
521 If the form is being displayed for the first time (or has failed
522 validation and it being redisplayed), we use
523 C<$form-E<gt>default_from_model> to populate the form with data from the
528 Then, edit C<root/src/books/list.tt2> and add a new link below the
529 existing "Delete" link that allows us to edit/update each existing book.
530 The last E<lt>tdE<gt> cell in the book list table should look like the
534 [% # Add a link to delete a book %]
535 <a href="[% Catalyst.uri_for('delete', book.id) %]">Delete</a>
536 [% # Add a link to edit a book %]
537 <a href="[% Catalyst.uri_for('formfu_edit', book.id) %]">Edit</a>
541 =head2 Try Out the Edit/Update Feature
543 Press C<Ctrl-C> to kill the previous server instance (if it's still
544 running) and restart it:
546 $ script/myapp_server.pl
548 Make sure you are still logged in as C<test01> and go to the
549 L<http://localhost:3000/books/list> URL in your browser. Click the
550 "Edit" link next to "Internetworking with TCP/IP Vol. II", change the
551 rating to a 3, the "II" at end of the title to the number "2", add
552 Stevens as a co-author (control-click), and click Submit. You will then
553 be returned to the book list with a "Book edited" message at the top in
554 green. Experiment with other edits to various books.
557 =head2 Config::General Config for this tutorial
559 If you are having difficulty with YAML config above, please save the
560 below into the file C<formfu_create.conf> and delete the
561 C<formfu_create.yml> file. The below is in
562 L<Config::General|Config::General> format which follows the syntax of
567 constraints SingleValue
572 message Length must be between 5 and 40 characters
580 title Enter a book title here
584 constraints SingleValue
592 title Enter a rating between 1 and 5 here
616 Kennedy Clark, C<hkclark@gmail.com>
618 Please report any errors, issues or suggestions to the author. The
619 most recent version of the Catalyst Tutorial can be found at
620 L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Manual/lib/Catalyst/Manual/Tutorial/>.
622 Copyright 20066-2008, Kennedy Clark, under Creative Commons License
623 (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).