X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FAdvancedCRUD%2FFormFu.pod;h=1c7176d1b0c8236933c715f9e38ad374ac7213f2;hp=af75d429a042ec3062b96e50cff8e19b22b6a715;hb=3b1fa91be1d89d2297aa9e8e83462344d9cd9820;hpb=0171092fe9723f2ccf838575434ae26cd841bcfd diff --git a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD/FormFu.pod b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD/FormFu.pod index af75d42..1c7176d 100644 --- a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD/FormFu.pod +++ b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD/FormFu.pod @@ -1,13 +1,11 @@ =head1 NAME -Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Part 9: Advanced CRUD - FormFu +Catalyst::Manual::Tutorial::AdvancedCRUD::FormFu - Catalyst Tutorial - Chapter 9: Advanced CRUD - FormFu -NOTE: This part of the tutorial is in progress and will be ready soon. - =head1 OVERVIEW -This is B for the Catalyst tutorial. +This is B for the Catalyst tutorial. L @@ -60,7 +58,8 @@ L This portion of the tutorial explores L and how it can be used to manage forms, perform validation of form input, -as well as save and restore data to/from the database. +as well as save and restore data to/from the database. This was written +using HTML::FormFu version 0.03007. See L @@ -68,43 +67,47 @@ for additional form management options other than L. -=head1 Install C +=head1 Install HTML::FormFu -If you are following along in Ubuntu, it turns out that C -is not yet available as a package at the time this was written. To -install it with a combination of C packages and traditional -CPAN modules, first use C to install most of the modules -required by C: +If you are following along in Debian 5, it turns out that some of the +modules we need are not yet available as Debian packages at the time +this was written. To install it with a combination of Debian packages +and traditional CPAN modules, first use C to install most of +the modules: - sudo apt-get install libtest-nowarnings-perl libdatetime-format-builder-perl \ - libdatetime-format-strptime-perl libdatetime-locale-perl \ - libhtml-tokeparser-simple-perl liblist-moreutils-perl \ - libregexp-copy-perl libregexp-common-perl libyaml-syck-perl libparams-util-perl +we need to install the +L package: + + sudo aptitude -y install libhtml-formfu-perl libmoose-perl \ + libregexp-assemble-perl libhtml-formfu-model-dbic-perl + + ... + + sudo aptitude clean Then use the following command to install directly from CPAN the modules -that aren't available as Ubuntu/Debian packages via C: +that aren't available as Debian packages: - sudo cpan File::ShareDir Task::Weaken Config::Any HTML::FormFu \ - Catalyst::Controller::HTML::FormFu + sudo cpan Catalyst::Component::InstancePerContext Catalyst::Controller::HTML::FormFu -=head1 C FORM CREATION +=head1 HTML::FormFu FORM CREATION This section looks at how L can be used to -add additional functionality to the manually created form from Part 4. +add additional functionality to the manually created form from Chapter 4. -=head2 Inherit From C +=head2 Inherit From Catalyst::Controller::HTML::FormFu First, change your C to inherit from L -by changing the C line from the default of: +by changing the C line from the default of: - use base 'Catalyst::Controller'; + use parent 'Catalyst::Controller'; to use the FormFu base controller class: - use base 'Catalyst::Controller::HTML::FormFu'; + use parent 'Catalyst::Controller::HTML::FormFu'; =head2 Add Action to Display and Save the Form @@ -118,31 +121,31 @@ following method: =cut - sub formfu_create :Local :FormConfig { + sub formfu_create :Chained('base') :PathPart('formfu_create') :Args(0) :FormConfig { my ($self, $c) = @_; # Get the form that the :FormConfig attribute saved in the stash my $form = $c->stash->{form}; - # Check if the form as been submitted (vs. displaying the initial - # form) and if the data based validation. "submitted_and_valid" + # Check if the form has been submitted (vs. displaying the initial + # form) and if the data passed validation. "submitted_and_valid" # is shorthand for "$form->submitted && !$form->has_errors" if ($form->submitted_and_valid) { # Create a new book - my $book = $c->model('DB::Books')->new_result({}); + my $book = $c->model('DB::Book')->new_result({}); # Save the form data for the book - $form->save_to_model($book); + $form->model->update($book); # Set a status message for the user $c->flash->{status_msg} = 'Book created'; # Return to the books list - $c->response->redirect($c->uri_for('list')); + $c->response->redirect($c->uri_for($self->action_for('list'))); $c->detach; } else { # Get the authors from the DB - my @authorObjs = $c->model("DB::Authors")->all(); + my @author_objs = $c->model("DB::Author")->all(); # Create an array of arrayrefs where each arrayref is an author my @authors; - foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) { + foreach (sort {$a->last_name cmp $b->last_name} @author_objs) { push(@authors, [$_->id, $_->last_name]); } # Get the select added by the config file @@ -203,15 +206,16 @@ following text: value: Submit B Copying and pasting YAML from perl documentation is sometimes -tricky. See the L section of -this document for a foolproof procedure. +tricky. See the L section of +this document for a more foolproof config format. =head2 Update the CSS -Edit C and add the following lines to the bottom of +Edit C and add the following lines to the bottom of the file: + ... input { display: block; } @@ -239,7 +243,7 @@ Open C in your editor and enter the following: [%# Render the HTML::FormFu Form %] [% form %] -

Return to book list

+

Return to book list

=head2 Add Links for Create and Update via C @@ -247,16 +251,17 @@ Open C in your editor and enter the following: Open C in your editor and add the following to the bottom of the existing file: + ...

HTML::FormFu: - Create + Create

This adds a new link to the bottom of the book list page that we can use to easily launch our HTML::FormFu-based form. -=head2 Test The Create Form +=head2 Test The HTML::FormFu Create Form Press C to kill the previous server instance (if it's still running) and restart it: @@ -265,17 +270,21 @@ running) and restart it: Login as C (password: mypass). Once at the Book List page, click the new HTML::FormFu "Create" link at the bottom to display the -form. Fill in the following values: Title = "Internetworking with -TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit, -and you will be returned to the Book List page with a "Book created" -status message displayed. +form. Fill in the following values: -Also note that this implementation allows you to can create books with + Title = "Internetworking with TCP/IP Vol. II" + Rating = "4" + Author = "Comer" + +Click the Submit button, and you will be returned to the Book List page +with a "Book created" status message displayed. + +Also note that this implementation allows you to create books with any bogus information. Although we have constrained the authors with the drop-down list (note that this isn't bulletproof because we still have not prevented a user from "hacking" the form to specify other values), there are no restrictions on items such as the length of the title (for -example, you can create a one-letter title) and value for the rating +example, you can create a one-letter title) and the value of the rating (you can use any number you want, and even non-numeric values with SQLite). The next section will address this concern. @@ -286,7 +295,7 @@ the previous paragraph is that the I itself is not performing any validation. -=head1 C VALIDATION AND FILTERING +=head1 HTML::FormFu VALIDATION AND FILTERING Although the use of L in the previous section did provide an automated mechanism to build the form, the real power of @@ -315,16 +324,8 @@ to match: # This is an optional 'mouse over' title pop-up attributes: title: Enter a book title here - # Use Filter to clean up the input data - filter: - # Remove whitespace at both ends - - TrimEdges - # Escape HTML characters for safety - - HTMLEscape # Add constraints for the field constraints: - # The user cannot leave this field blank - - SingleValue # Force the length to be between 5 and 40 chars - type: Length min: 5 @@ -339,16 +340,22 @@ to match: attributes: title: Enter a rating between 1 and 5 here # Use Filter to clean up the input data - filter: - # Remove whitespace at both ends - - TrimEdges + # Could use 'NonNumeric' below, but since Filters apply *before* + # constraints, it would conflict with the 'Integer' constraint below. + # So let's skip this and just use the constraint. + #filter: # Remove everything except digits - - NonNumeric + #- NonNumeric # Add constraints to the field constraints: - - SingleValue # Make sure it's a number - - Integer + - type: Integer + message: "Required. Digits only, please." + # Check the min & max values + - type: Range + min: 1 + max: 5 + message: "Must be between 1 and 5." # Add a select list for the author selection. Note that we will # dynamically fill in all the authors from the controller but we @@ -366,12 +373,6 @@ to match: # One could argue we don't need to do filters or constraints for # a select list, but it's smart to do validation and sanity # checks on this data in case a user "hacks" the input - # Use Filter to clean up the input data - filter: - # Remove whitespace at both ends - - TrimEdges - # Escape HTML characters for safety - - HTMLEscape # Add constraints to the field constraints: # Make sure it's a number @@ -382,14 +383,21 @@ to match: name: submit value: Submit - # Globally ensure that each field only specified one value + # Global filters and constraints. constraints: - # The user cannot leave any fields blank - - Required + # The user cannot leave any fields blank + - Required + # If not all fields are required, move the Required constraint to the + # fields that are + filter: + # Remove whitespace at both ends + - TrimEdges + # Escape HTML characters for safety + - HTMLEscape B Copying and pasting YAML from perl documentation is sometimes -tricky. See the L section of -this document for a foolproof procedure. +tricky. See the L section of +this document for a more foolproof config format. The main changes are: @@ -444,39 +452,40 @@ bottom: =cut - sub formfu_edit :Local :FormConfig('books/formfu_create.yml') { - my ($self, $c, $id) = @_; + sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0) + :FormConfig('books/formfu_create.yml') { + my ($self, $c) = @_; - # Get the specified book - my $book = $c->model('DB::Books')->find($id); + # Get the specified book already saved by the 'object' method + my $book = $c->stash->{object}; # Make sure we were able to get a book unless ($book) { $c->flash->{error_msg} = "Invalid book -- Cannot edit"; - $c->response->redirect($c->uri_for('list')); + $c->response->redirect($c->uri_for($self->action_for('list'))); $c->detach; } # Get the form that the :FormConfig attribute saved in the stash my $form = $c->stash->{form}; - # Check if the form as been submitted (vs. displaying the initial - # form) and if the data based validation. "submitted_and_valid" + # Check if the form has been submitted (vs. displaying the initial + # form) and if the data passed validation. "submitted_and_valid" # is shorthand for "$form->submitted && !$form->has_errors" if ($form->submitted_and_valid) { # Save the form data for the book - $form->save_to_model($book); + $form->model->update($book); # Set a status message for the user $c->flash->{status_msg} = 'Book edited'; # Return to the books list - $c->response->redirect($c->uri_for('list')); + $c->response->redirect($c->uri_for($self->action_for('list'))); $c->detach; } else { # Get the authors from the DB - my @authorObjs = $c->model("DB::Authors")->all(); + my @author_objs = $c->model("DB::Author")->all(); # Create an array of arrayrefs where each arrayref is an author my @authors; - foreach (sort {$a->last_name cmp $b->last_name} @authorObjs) { + foreach (sort {$a->last_name cmp $b->last_name} @author_objs) { push(@authors, [$_->id, $_->last_name]); } # Get the select added by the config file @@ -484,7 +493,7 @@ bottom: # Add the authors to it $select->options(\@authors); # Populate the form with existing values from DB - $form->defaults_from_model($book); + $form->model->default_values($book); } # Set the template @@ -499,28 +508,36 @@ the common code in separate methods). The main differences are: =item * -We accept C<$id> as an argument via the URL. +We have to manually specify the name of the FormFu .yml file as an +argument to C<:FormConfig> because the name can no longer be +automatically deduced from the name of our action/method (by default, +FormFu would look for a file named C). =item * -We use C<$id> to look up the existing book from the database. +We load the book object from the stash (found using the $id passed to +the Chained object method) =item * -We make sure the C<$id> and book lookup returned a valid book. If not, -we set the error message and return to the book list. +We use C<$id> to look up the existing book from the database. + +=item * +We make sure the book lookup returned a valid book. If not, we set +the error message and return to the book list. + =item * If the form has been submitted and passes validation, we skip creating a -new book and just use C<$form-Esave_to_model> to update the existing +new book and just use C<$form-Emodel-Eupdate> to update the existing book. =item * If the form is being displayed for the first time (or has failed validation and it being redisplayed), we use - C<$form-Edefault_from_model> to populate the form with data from the + C<$form-Emodel-Edefault_values> to populate the form with data from the database. =back @@ -530,12 +547,18 @@ existing "Delete" link that allows us to edit/update each existing book. The last EtdE cell in the book list table should look like the following: + ... [% # Add a link to delete a book %] - Delete + Delete [% # Add a link to edit a book %] - Edit + Edit + ... + +B Only add two lines (the "Add a link to edit a book" comment +and the href for C). Make sure you add it below the +existing C link. =head2 Try Out the Edit/Update Feature @@ -554,139 +577,91 @@ be returned to the book list with a "Book edited" message at the top in green. Experiment with other edits to various books. -=head2 Copy-Paste versions of the YAML Config. - -YAML, the config format used in formfu depends on whitespace and can -behave strangely in perl documentation because POD is also whitespace -dependent. If you copy and paste the YAML below into the file -C and then run the following perl -oneliner, you're pretty much guaranteed to get valid YAML: - - $ perl -p -i -e 's/\s+\|//g' root/forms/books/formfu_create.yml - -=head2 YAML for the first half of the tutorial: - - |--- - |# indicator is the field that is used to test for form submission - |indicator: submit - |# Start listing the form elements - |elements: - | # The first element will be a text field for the title - | - type: Text - | name: title - | label: Title - | # This is an optional 'mouse over' title pop-up - | attributes: - | title: Enter a book title here - | - | # Another text field for the numeric rating - | - type: Text - | name: rating - | label: Rating - | attributes: - | title: Enter a rating between 1 and 5 here - | - | # Add a drop-down list for the author selection. Note that we will - | # dynamically fill in all the authors from the controller but we - | # could manually set items in the drop-list by adding this YAML code: - | # options: - | # - [ '1', 'Bastien' ] - | # - [ '2', 'Nasseh' ] - | - type: Select - | name: authors - | label: Author - | - | # The submit button - | - type: Submit - | name: submit - | value: Submit - - -=head2 YAML for the second part of the tutorial - - |--- - |# indicator is the field that is used to test for form submission - |indicator: submit - |# Start listing the form elements - |elements: - | # The first element will be a text field for the title - | - type: Text - | name: title - | label: Title - | # This is an optional 'mouse over' title pop-up - | attributes: - | title: Enter a book title here - | # Use Filter to clean up the input data - | filter: - | # Remove whitespace at both ends - | - TrimEdges - | # Escape HTML characters for safety - | - HTMLEscape - | # Add constraints for the field - | constraints: - | # The user cannot leave this field blank - | - SingleValue - | # Force the length to be between 5 and 40 chars - | - type: Length - | min: 5 - | max: 40 - | # Override the default of 'Invalid input' - | message: Length must be between 5 and 40 characters - | - | # Another text field for the numeric rating - | - type: Text - | name: rating - | label: Rating - | attributes: - | title: Enter a rating between 1 and 5 here - | # Use Filter to clean up the input data - | filter: - | # Remove whitespace at both ends - | - TrimEdges - | # Remove everything except digits - | - NonNumeric - | # Add constraints to the field - | constraints: - | - SingleValue - | # Make sure it's a number - | - Integer - | - | # Add a select list for the author selection. Note that we will - | # dynamically fill in all the authors from the controller but we - | # could manually set items in the select by adding this YAML code: - | # options: - | # - [ '1', 'Bastien' ] - | # - [ '2', 'Nasseh' ] - | - type: Select - | name: authors - | label: Author - | # Convert the drop-down to a multi-select list - | multiple: 1 - | # Display 3 entries (user can scroll to see others) - | size: 3 - | # One could argue we don't need to do filters or constraints for - | # a select list, but it's smart to do validation and sanity - | # checks on this data in case a user "hacks" the input - | # Use Filter to clean up the input data - | filter: - | # Remove whitespace at both ends - | - TrimEdges - | # Escape HTML characters for safety - | - HTMLEscape - | # Add constraints to the field - | constraints: - | # Make sure it's a number - | - Integer - | - | # The submit button - | - type: Submit - | name: submit - | value: Submit - | - |# Globally ensure that each field only specified one value - |constraints: - | # The user cannot leave any fields blank - | - Required +=head2 More Things to Try + +You are now armed with enough knowledge to be dangerous. You can keep +tweaking the example application; some things you might want to do: + +=over 4 + +=item * + +Add an appropriate authorization check to the new Edit function. + +=item * + +Cleanup the List page so that the Login link only displays when the user +isn't logged in and the Logout link only displays when a user is logged +in. + +=item * + +Add a more sensible policy for when and how users and admins can do +things in the CRUD cycle. + +=item * + +Support the CRUD cycle for authors. + +=back + +Or you can proceed to write your own application, which is probably the +real reason you worked through this Tutorial in the first place. + + +=head2 Config::General Config for this tutorial + +If you are having difficulty with YAML config above, please save the +below into the file C and delete the +C file. The below is in +L format which follows the syntax of +Apache config files. + + constraints Required + + + min 5 + max 40 + type Length + message Length must be between 5 and 40 characters + + filter TrimEdges + filter HTMLEscape + name title + type Text + label Title + + title Enter a book title here + + + + constraints Integer + filter TrimEdges + filter NonNumeric + name rating + type Text + label Rating + + title Enter a rating between 1 and 5 here + + + + constraints Integer + filter TrimEdges + filter HTMLEscape + name authors + type Select + label Author + multiple 1 + size 3 + + + value Submit + name submit + type Submit + + indicator submit + =head1 AUTHOR @@ -694,8 +669,7 @@ Kennedy Clark, C Please report any errors, issues or suggestions to the author. The most recent version of the Catalyst Tutorial can be found at -L. +L. -Copyright 20066-2008, Kennedy Clark, under Creative Commons License -(L). - +Copyright 2006-2008, Kennedy Clark, under Creative Commons License +(L).