X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FTutorial%2FAdvancedCRUD.pod;fp=lib%2FCatalyst%2FManual%2FTutorial%2FTutorial%2FAdvancedCRUD.pod;h=77b81cd7f4243b05163135ff4c00032ffbb2d368;hp=0000000000000000000000000000000000000000;hb=d442cc9fbdf45a28fe02f13552f44e3e2f7ec22e;hpb=4be096acef2b81af64442da618294f69630029a3 diff --git a/lib/Catalyst/Manual/Tutorial/Tutorial/AdvancedCRUD.pod b/lib/Catalyst/Manual/Tutorial/Tutorial/AdvancedCRUD.pod new file mode 100644 index 0000000..77b81cd --- /dev/null +++ b/lib/Catalyst/Manual/Tutorial/Tutorial/AdvancedCRUD.pod @@ -0,0 +1,764 @@ +=head1 NAME + +Catalyst::Manual::Tutorial::AdvancedCRUD - Catalyst Tutorial - Part 8: Advanced CRUD + + +=head1 OVERVIEW + +This is B for the Catalyst tutorial. + +L + +=over 4 + +=item 1 + +L + +=item 2 + +L + +=item 3 + +L + +=item 4 + +L + +=item 5 + +L + +=item 6 + +L + +=item 7 + +L + +=item 8 + +B + +=item 9 + +L + +=back + +=head1 DESCRIPTION + +This part of the tutorial explores more advanced functionality for +Create, Read, Update, and Delete (CRUD) than we saw in Part 3. In +particular, it looks at a number of techniques that can be useful for +the Update portion of CRUD, such as automated form generation, +validation of user-entered data, and automated transfer of data between +forms and model objects. + +In keeping with the Catalyst (and Perl) spirit of flexibility, there are +many different ways to approach advanced CRUD operations in a Catalyst +environment. One alternative is to use +L +to instantly construct a set of Controller methods and templates for +basic CRUD operations. Although a popular subject in Quicktime +movies that serve as promotional material for various frameworks, +real-world applications generally require more control. Other +options include L and +L. + +Here, we will make use of the L to not only +ease form creation, but to also provide validation of the submitted +data. The approached used by this part of the tutorial is to slowly +incorporate additional L functionality in a +step-wise fashion (we start with fairly simple form creation and then +move on to more complex and "magical" features such as validation and +auto-population/auto-saving). + +B Part 8 of the tutorial is optional. Users who do not wish to +use L may skip this part. + +You can checkout the source code for this example from the catalyst subversion repository as per the instructions in L + +=head1 C FORM CREATION + +This section looks at how L can be used to +add additional functionality to the manually created form from Part 3. + +=head2 Add the C Plugin + +Open C in your editor and add the following to the list of +plugins (be sure to leave the existing plugins enabled): + + HTML::Widget + +=head2 Add a Form Creation Helper Method + +Open C in your editor and add the +following method: + + =head2 make_book_widget + + Build an HTML::Widget form for book creation and updates + + =cut + + sub make_book_widget { + my ($self, $c) = @_; + + # Create an HTML::Widget to build the form + my $w = $c->widget('book_form')->method('post'); + + # Get authors + my @authorObjs = $c->model("MyAppDB::Author")->all(); + my @authors = map {$_->id => $_->last_name } + sort {$a->last_name cmp $b->last_name} @authorObjs; + + # Create the form feilds + $w->element('Textfield', 'title' )->label('Title')->size(60); + $w->element('Textfield', 'rating' )->label('Rating')->size(1); + $w->element('Select', 'authors')->label('Authors') + ->options(@authors); + $w->element('Submit', 'submit' )->value('submit'); + + # Return the widget + return $w; + } + +This method provides a central location that builds an +HTML::Widget-based form with the appropriate fields. The "Get authors" +code uses DBIC to retrieve a list of model objects and then uses C +to create a hash where the hash keys are the database primary keys from +the authors table and the associated values are the last names of the +authors. + +=head2 Add Actions to Display and Save the Form + +Open C in your editor and add the +following methods: + + =head2 hw_create + + Build an HTML::Widget form for book creation and updates + + =cut + + sub hw_create : Local { + my ($self, $c) = @_; + + # Create the widget and set the action for the form + my $w = $self->make_book_widget($c); + $w->action($c->uri_for('hw_create_do')); + + # Write form to stash variable for use in template + $c->stash->{widget_result} = $w->result; + + # Set the template + $c->stash->{template} = 'books/hw_form.tt2'; + } + + + =head2 hw_create_do + + Build an HTML::Widget form for book creation and updates + + =cut + + sub hw_create_do : Local { + my ($self, $c) = @_; + + # Retrieve the data from the form + my $title = $c->request->params->{title}; + my $rating = $c->request->params->{rating}; + my $authors = $c->request->params->{authors}; + + # Call create() on the book model object. Pass the table + # columns/field values we want to set as hash values + my $book = $c->model('MyAppDB::Book')->create({ + title => $title, + rating => $rating + }); + + # Add a record to the join table for this book, mapping to + # appropriate author + $book->add_to_book_authors({author_id => $authors}); + + # Set a status message for the user + $c->stash->{status_msg} = 'Book created'; + + # Use 'hw_create' to redisplay the form. As discussed in + # Part 3, 'detach' is like 'forward', but it does not return + $c->detach('hw_create'); + } + +Note how we use C to build the core parts of the form +in one location, but we set the action (the URL the form is sent to when +the user clicks the 'Submit' button) separately in C. Doing +so allows us to have the same form submit the data to different actions +(e.g., C for a create operation but C to +update an existing book object). + +B If you receive an error about Catalyst not being able to find +the template C, please verify that you followed the +instructions in the final section of +L where +you returned to a manually-specified template. You can either use +C/C B default template names, but the two cannot +be used together. + + +=head2 Update the CSS + +Edit C and add the following lines to the bottom of +the file: + + label { + display: block; + width: 10em; + position: relative; + margin: .5em 0em; + } + label input { + position: absolute; + left: 100%; + } + label select { + position: absolute; + left: 100%; + } + .submit { + margin-top: 2em;; + } + .error_messages { + color: [% site.col.error %]; + } + +These changes will display form elements vertically and also show error +messages in red. Note that we are pulling the color scheme settings +from the C file that was created by the TTSite +helper. This allows us to change the color used by various error styles +in the CSS from a single location. + +=head2 Create a Template Page To Display The Form + +Open C in your editor and enter the following: + + [% META title = 'Create/Update Book' %] + + [% widget_result.as_xml %] + +

Return to book list

+ +=head2 Add Links for Create and Update via C + +Open C in your editor and add the following to +the bottom of the existing file: + +

+ HTML::Widget: + Create +

+ + +=head2 Test The Create Form + +Press C to kill the previous server instance (if it's still +running) and restart it: + + $ script/myapp_server.pl + +Login as C. Once at the Book List page, click the HTML::Widget +"Create" link to display for form produced by C. Fill +out the form with the following values: Title = "Internetworking with +TCP/IP Vol. II", Rating = "4", and Author = "Comer". Click Submit, and +you will be returned to the Create/Update Book page with a "Book +created" status message displayed. Click "Return to book list" to view +the newly created book on the main list. + +Also note that this implementation allows you to can create books with +bogus information. Although we have constrained the authors with the +drop-down list, 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 (you can use any number you want, and even non-numeric values +with SQLite). The next section will address this concern. + +B Depending on the database you are using and how you established +the columns in your tables, the database could obviously provide various +levels of "type enforcement" on your data. The key point being made in +the previous paragraph is that the I itself is not +performing any validation. + +=head1 C 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 +this module stems from functionality that can automatically validate and +filter the user input. Validation uses constraints to be sure that +users input appropriate data (for example, that the email field of a +form contains a valid email address). Filtering can be used to remove +extraneous whitespace from fields or to escape meta-characters in user +input. + +=head2 Add Constraints and Filters to the Widget Creation Method + +Open C in your editor and update the +C method to match the following (new sections have +been marked with a C<*** NEW:> comment): + + sub make_book_widget { + my ($self, $c) = @_; + + # Create an HTML::Widget to build the form + my $w = $c->widget('book_form')->method('post'); + + # Get authors + my @authorObjs = $c->model("MyAppDB::Author")->all(); + my @authors = map {$_->id => $_->last_name } + sort {$a->last_name cmp $b->last_name} @authorObjs; + + # Create the form feilds + $w->element('Textfield', 'title' )->label('Title')->size(60); + $w->element('Textfield', 'rating' )->label('Rating')->size(1); + # ***NEW: Convert to multi-select list + $w->element('Select', 'authors')->label('Authors') + ->options(@authors)->multiple(1)->size(3); + $w->element('Submit', 'submit' )->value('submit'); + + # ***NEW: Set constraints + $w->constraint(All => qw/title rating authors/) + ->message('Required. '); + $w->constraint(Integer => qw/rating/) + ->message('Must be an integer. '); + $w->constraint(Range => qw/rating/)->min(1)->max(5) + ->message('Must be a number between 1 and 5. '); + $w->constraint(Length => qw/title/)->min(5)->max(50) + ->message('Must be between 5 and 50 characters. '); + + # ***NEW: Set filters + for my $column (qw/title rating authors/) { + $w->filter( HTMLEscape => $column ); + $w->filter( TrimEdges => $column ); + } + + # Return the widget + return $w; + } + +The main changes are: + +=over 4 + +=item * + +The C