X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FTutorial%2FAdvancedCRUD.pod;h=6e157e0dece38d4e5ccef1124c662d2cb217a4b4;hb=c9b77c06a0de97f1d6e9a66091e693a637578357;hp=45431785118d95f795fc60e889390eb1b034ce40;hpb=3c098c712dbc92de8efb97acf71772b439956cd9;p=catagits%2FCatalyst-Runtime.git diff --git a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod index 4543178..6e157e0 100644 --- a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod +++ b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod @@ -58,36 +58,36 @@ 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 approach advanced CRUD operations in a Catalyst +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, more real-world applications 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 the 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 +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 the 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 section. +use L may skip this part. B: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command: - svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@### - IMPORTANT: Does not work yet. Will be completed for final version. + svn co http://dev.catalyst.perl.org/repos/Catalyst/tags/examples/Tutorial/MyApp/5.7/AdvancedCRUD MyApp =head1 C FORM CREATION -This section looks at how L can be used to +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 @@ -110,7 +110,7 @@ following method: sub make_book_widget { my ($self, $c) = @_; - + # Create an HTML::Widget to build the form my $w = $c->widget('book_form')->method('post'); @@ -125,17 +125,17 @@ following method: $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 (so it can be called by multiple -actions, such as C and C) that builds an HTML::Wiget-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. +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 @@ -193,7 +193,6 @@ following methods: # Use 'hw_create' to redisplay the form $c->detach('hw_create'); - } Note how we use C to build the core parts of the form @@ -237,7 +236,8 @@ in the CSS from a single location. =head2 Create a Template Page To Display The Form -C +Open C in your editor and enter the following: + [% META title = 'Create/Update Book' %] [% widget_result.as_xml %] @@ -302,7 +302,7 @@ 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'); @@ -310,7 +310,7 @@ been marked with a C<*** NEW:> comment): 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); @@ -428,6 +428,8 @@ now has a C<$c-Estash-E{template}> line). Note that if we process the form in C I forward/detach back to , we would end up with C being called twice, resulting in a duplicate set of elements being added to the form. +(There are other ways to address the "duplicate form rendering" issue -- +just be aware that it exists.) =item * @@ -447,7 +449,8 @@ similar to the prior version of the C method. =head2 Try Out the Form -Press C to kill the previous server instance (if it's still running) and restart it: +Press C to kill the previous server instance (if it's still +running) and restart it: $ script/myapp_server.pl @@ -457,6 +460,7 @@ two, and zero authors. When you click Submit, the HTML::Widget C items will validate the logic and insert feedback as appropriate. + =head1 Enable C Support In this section we will take advantage of some of the "auto-population" @@ -468,7 +472,7 @@ model classes: =item * -fill_wiget() +fill_widget() Takes data from the database and transfers it to your form widget. @@ -540,6 +544,9 @@ match the following code: # Set a status message for the user $c->stash->{status_msg} = 'Book created'; + + # Redisplay an empty form for another + $c->stash->{widget_result} = $w->result; } # Set the template @@ -552,18 +559,200 @@ C<$c-Emodel('MyAppDB::Book')-Ecreate> and replaced it with a single call to C<$book-Epopulate_from_widget>. Note that we still have to call C<$book-Eadd_to_book_authors> once per author because C does not currently handle the relationships -between tables. +between tables. Also, we reset the form to an empty fields by adding +another call to C<$w-Eresult> and storing the output in the stash +(if we don't override the output from C<$w-Eprocess($c-Ereq)>, +the form values already entered will be retained on redisplay -- +although this could be desirable for some applications, we avoid it +here to help avoid the creation of duplicate records). -=head1 AUTHOR -Kennedy Clark, C +=head2 Try Out the Form + +Press C to kill the previous server instance (if it's still +running) and restart it: + + $ script/myapp_server.pl + +Try adding a book that validate. Return to the book list and the book +you added should be visible. + + + +=head1 Rendering C Forms in a Table + +Some developers my wish to use the "old-fashioned" table style of +rendering a form in lieu of the default C rendering that +assumes you will use CSS for formatting. This section demonstrates +some techniques that can override the default rendering with a +custom class. + + +=head2 Add a New "Element Container" + +Open C in your editor and enter: + + package FormElementContainer; + + use base 'HTML::Widget::Container'; + + sub _build_element { + my ($self, $element) = @_; + + return () unless $element; + if (ref $element eq 'ARRAY') { + return map { $self->_build_element($_) } @{$element}; + } + my $e = $element->clone; + my $class = $e->attr('class') || ''; + $e = new HTML::Element('span', class => 'fields_with_errors')->push_content($e) + if $self->error && $e->tag eq 'input'; + + return $e ? ($e) : (); + } + + 1; + +This simply dumps the HTML code for a given form element, followed by a +C that can contain validation error message. + -Please report any errors, issues or suggestions to the author. +=head2 Enable the New Element Container When Building the Form -Copyright 2006, Kennedy Clark. All rights reserved. +Open C in your editor. First add a +C for your element container class: -This library is free software; you can redistribute it and/or modify it -under the same terms as Perl itself. + use FormElementContainer; + +B If you forget to C your container class in your +controller, then your form will not be displayed and no error messages +will be generated. Don't forget this important step! + +Then tell C to use that class during rendering by updating +C to match the following: + + sub make_book_widget { + my ($self, $c) = @_; + + # Create an HTML::Widget to build the form + my $w = $c->widget('book_form')->method('post'); + + # ***New: Use custom class to render each element in the form + $w->element_container_class('FormElementContainer'); + + # 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); + # Convert to multi-select list + $w->element('Select', 'authors')->label('Authors') + ->options(@authors)->multiple(1)->size(3); + $w->element('Submit', 'submit' )->value('submit'); + + # 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. '); + + # Set filters + for my $column (qw/title rating authors/) { + $w->filter( HTMLEscape => $column ); + $w->filter( TrimEdges => $column ); + } + + # Return the widget + return $w; + } + +The two new lines are marked with C<***New:>. + + +=head2 Update the TT Template + +Open C and edit it to match: + + [% META title = 'Create/Update Book' %] + + [%# Comment out the auto-rendered form %] + [%# widget_result.as_xml %] + + + [%# Iterate over the form elements and display each -%] +
+ + [% FOREACH element = widget_result.elements %] + + + + + [% END %] +
+ [% element.label.as_text %] + + [% element.element_xml %] + + [% element.error_xml %] + +
+
+ + +

Return to book list

+ + + [%# A little JavaScript to move the cursor to the first field %] + + +This represents three changes: + +=over 4 + +=item * + +The existing C has been commented out. + +=item * + +It loops through each form element, displaying the field name in the +first table cell along with the form element and validation errors in +the second field. + +=item * + +JavaScript to position the user's cursor in the first field of the form. + +=back + + +=head2 Try Out the Form + +Press C to kill the previous server instance (if it's still +running) and restart it: + + $ script/myapp_server.pl + +Try adding a book that validate. Return to the book list and the book +you added should be visible. + + +=head1 AUTHOR + +Kennedy Clark, C -Version: .94 +Please report any errors, issues or suggestions to the author. The +most recent version of the Catalyst Tutorial can be found at +L. +Copyright 2006, Kennedy Clark, under Creative Commons License +(L).