From: Dan Dascalescu Date: Mon, 9 Mar 2009 09:02:39 +0000 (+0000) Subject: Fixed typos and missing words in BasicCRUD X-Git-Tag: v5.8005~184 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=commitdiff_plain;h=554908172d7b73d43084ac283bb2dad6c7283e92 Fixed typos and missing words in BasicCRUD --- diff --git a/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod b/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod index 46f886e..1ae6394 100644 --- a/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod +++ b/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod @@ -66,12 +66,12 @@ focus on the Create and Delete aspects of CRUD. More advanced capabilities, including full Update functionality, will be addressed in Part 9. -Although this part of the tutorial will show you how to build CRUD -functionality yourself, another option is to use a "CRUD builder" type -of tool to automate the process. You get less control, but it's quick -and easy. For example, see +Although this part of the tutorial will show you how to build CRUD +functionality yourself, another option is to use a "CRUD builder" type +of tool to automate the process. You get less control, but it's quick +and easy. For example, see L, -L, and +L, and L. You can checkout the source code for this example from the catalyst @@ -81,7 +81,7 @@ L. =head1 FORMLESS SUBMISSION -Our initial attempt at object creation will utilize the "URL +Our initial attempt at object creation will utilize the "URL arguments" feature of Catalyst (we will employ the more common form- based submission in the sections that follow). @@ -91,42 +91,42 @@ based submission in the sections that follow). Edit C and enter the following method: =head2 url_create - + Create a book with the supplied title, rating, and author - + =cut - + sub url_create : Local { - # In addition to self & context, get the title, rating, & - # author_id args from the URL. Note that Catalyst automatically - # puts extra information after the "///model('DB::Books')->create({ title => $title, rating => $rating }); - - # Add a record to the join table for this book, mapping to + + # Add a record to the join table for this book, mapping to # appropriate author $book->add_to_book_authors({author_id => $author_id}); # Note: Above is a shortcut for this: # $book->create_related('book_authors', {author_id => $author_id}); - + # Assign the Book object to the stash for display in the view $c->stash->{book} = $book; - + # This is a hack to disable XSUB processing in Data::Dumper # (it's used in the view). This is a work-around for a bug in # the interaction of some versions or Perl, Data::Dumper & DBIC. # You won't need this if you aren't using Data::Dumper (or if - # you are running DBIC 0.06001 or greater), but adding it doesn't + # you are running DBIC 0.06001 or greater), but adding it doesn't # hurt anything either. $Data::Dumper::Useperl = 1; - + # Set the TT template to use $c->stash->{template} = 'books/create_done.tt2'; } @@ -148,43 +148,43 @@ Edit C and then enter: [% # Not a good idea for production use, though. :-) 'Indent=1' is -%] [% # optional, but prevents "massive indenting" of deeply nested objects -%] [% USE Dumper(Indent=1) -%] - + [% # Set the page title. META can 'go back' and set values in templates -%] [% # that have been processed 'before' this template (here it's for -%] [% # root/lib/site/html and root/lib/site/header). Note that META on -%] [% # simple strings (e.g., no variable interpolation). -%] [% META title = 'Book Created' %] - + [% # Output information about the record that was added. First title. -%]

Added book '[% book.title %]' - + [% # Output the last name of the first author. This is complicated by an -%] [% # issue in TT 2.15 where blessed hash objects are not handled right. -%] [% # First, fetch 'book.authors' from the DB once. -%] [% authors = book.authors %] [% # Now use IF statements to test if 'authors.first' is "working". If so, -%] [% # we use it. Otherwise we use a hack that seems to keep TT 2.15 happy. -%] - by '[% authors.first.last_name IF authors.first; + by '[% authors.first.last_name IF authors.first; authors.list.first.value.last_name IF ! authors.first %]' - + [% # Output the rating for the book that was added -%] with a rating of [% book.rating %].

- + [% # Provide a link back to the list page -%] [% # 'uri_for()' builds a full URI; e.g., 'http://localhost:3000/books/list' -%]

Return to list

- + [% # Try out the TT Dumper (for development only!) -%]
     Dump of the 'book' variable:
     [% Dumper.dump(book) %]
     
-The TT C directive allows access to a variety of plugin modules -(TT plugins, that is, not Catalyst plugins) to add extra functionality -to the base TT capabilities. Here, the plugin allows -L "pretty printing" of objects and -variables. Other than that, the rest of the code should be familiar +The TT C directive allows access to a variety of plugin modules +(TT plugins, that is, not Catalyst plugins) to add extra functionality +to the base TT capabilities. Here, the plugin allows +L "pretty printing" of objects and +variables. Other than that, the rest of the code should be familiar from the examples in Part 3. @@ -209,44 +209,44 @@ Next, use your browser to enter the following URL: http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4 -Your browser should display "Added book 'TCPIP_Illustrated_Vol-2' by -'Stevens' with a rating of 5." along with a dump of the new book model -object as it was returned by DBIC. You should also see the following -DBIC debug messages displayed in the development server log messages +Your browser should display "Added book 'TCPIP_Illustrated_Vol-2' by +'Stevens' with a rating of 5." along with a dump of the new book model +object as it was returned by DBIC. You should also see the following +DBIC debug messages displayed in the development server log messages if you have DBIC_TRACE set: INSERT INTO books (rating, title) VALUES (?, ?): `5', `TCPIP_Illustrated_Vol-2' INSERT INTO book_authors (author_id, book_id) VALUES (?, ?): `4', `6' - SELECT author.id, author.first_name, author.last_name - FROM book_authors me JOIN authors author + SELECT author.id, author.first_name, author.last_name + FROM book_authors me JOIN authors author ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '6' The C statements are obviously adding the book and linking it to the existing record for Richard Stevens. The C @@ -510,19 +510,19 @@ Edit C and add the following method to save the form information to the database: =head2 form_create_do - + Take information from form and add to database - + =cut - + sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) { my ($self, $c) = @_; - + # Retrieve the values from the form my $title = $c->request->params->{title} || 'N/A'; my $rating = $c->request->params->{rating} || 'N/A'; my $author_id = $c->request->params->{author_id} || '1'; - + # Create the book my $book = $c->model('DB::Books')->create({ title => $title, @@ -530,14 +530,14 @@ save the form information to the database: }); # Handle relationship with author $book->add_to_book_authors({author_id => $author_id}); - + # Store new model object in stash $c->stash->{book} = $book; - + # Avoid Data::Dumper issue mentioned earlier - # You can probably omit this + # You can probably omit this $Data::Dumper::Useperl = 1; - + # Set the TT template to use $c->stash->{template} = 'books/create_done.tt2'; } @@ -550,7 +550,7 @@ it. Then restart the server: $ script/myapp_server.pl -Notice that the server startup log reflects the two new chained +Notice that the server startup log reflects the two new chained methods that we added: [debug] Loaded Chained actions: @@ -593,10 +593,10 @@ and 2) the four lines for the Delete link near the bottom). [% # see this "chomping" in your browser because HTML ignores blank lines, but -%] [% # it WILL eliminate a blank line if you view the HTML source. It's purely -%] [%- # optional, but both the beginning and the ending TT tags support chomping. -%] - + [% # Provide a title to root/lib/site/header -%] [% META title = 'Book List' -%] - + [% # Display each book in a table row %] @@ -607,11 +607,11 @@ and 2) the four lines for the Delete link near the bottom).
TitleRatingAuthor(s)Links
[% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%] [% # loop in 'side effect notation' to load just the last names of the -%] - [% # authors into the list. Note that the 'push' TT vmethod does not -%] + [% # authors into the list. Note that the 'push' TT vmethod doesn't return -%] [% # a value, so nothing will be printed here. But, if you have something -%] - [% # in TT that does return a method and you don't want it printed, you -%] - [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to -%] - [% # call it and discard the return value. -%] + [% # in TT that does return a value and you don't want it printed, you can -%] + [% # 1) assign it to a bogus value, or -%] + [% # 2) use the CALL keyword to call it and discard the return value. -%] [% tt_authors = [ ]; tt_authors.push(author.last_name) FOREACH author = book.authors %] [% # Now use a TT 'virtual method' to display the author count in parens -%] @@ -627,20 +627,20 @@ and 2) the four lines for the Delete link near the bottom). [% END -%]
-The additional code is obviously designed to add a new column to the -right side of the table with a C "button" (for simplicity, +The additional code is obviously designed to add a new column to the +right side of the table with a C "button" (for simplicity, links will be used instead of full HTML buttons). -Also notice that we are using a more advanced form of C than -we have seen before. Here we use -C<$c-Econtroller-Eaction_for> to automatically generate a URI -appropriate for that action based on the method we want to link to -while inserting the C value into the appropriate place. Now, -if you ever change C<:PathPart('delete')> in your controller method to -C<:PathPart('kill')>, then your links will automatically update -without any changes to your .tt2 template file. As long as the name -of your method does not changed ("delete" here), then your links will -still be correct. There are a few shortcuts and options when using +Also notice that we are using a more advanced form of C than +we have seen before. Here we use +C<$c-Econtroller-Eaction_for> to automatically generate a URI +appropriate for that action based on the method we want to link to +while inserting the C value into the appropriate place. Now, +if you ever change C<:PathPart('delete')> in your controller method to +C<:PathPart('kill')>, then your links will automatically update +without any changes to your .tt2 template file. As long as the name +of your method does not change ("delete" here), then your links will +still be correct. There are a few shortcuts and options when using C: =over 4 @@ -658,42 +658,42 @@ C<$c-Econtroller('_controller_name_')-Eaction_for('_method_name_')>. =back -B In practice you should B use a GET request to delete a -record -- always use POST for actions that will modify data. We are +B In practice you should B use a GET request to delete a +record -- always use POST for actions that will modify data. We are doing it here for illustrative and simplicity purposes only. =head2 Add a Common Method to Retrieve a Book for the Chain -As mentioned earlier, since we have a mixture of actions that operate -on a single book ID and others that do no, we should not have C -capture the book ID, find the corresponding book in the database and -save it in the stash for later links in the chain. However, just -because that logic does not belong in C doesn't mean that we -can't create another location to centralize the book lookup code. In -our case, we will create a method called C that will store the -specific book in the stash. Chains that always operate on a single -existing book can chain off this method, but methods such as -C that don't operate on an existing book can chain +As mentioned earlier, since we have a mixture of actions that operate +on a single book ID and others that do not, we should not have C +capture the book ID, find the corresponding book in the database and +save it in the stash for later links in the chain. However, just +because that logic does not belong in C doesn't mean that we +can't create another location to centralize the book lookup code. In +our case, we will create a method called C that will store the +specific book in the stash. Chains that always operate on a single +existing book can chain off this method, but methods such as +C that don't operate on an existing book can chain directly off base. To add the C method, edit C and add the following code: =head2 object - + Fetch the specified book object based on the book ID and store it in the stash - + =cut - + sub object :Chained('base') :PathPart('id') :CaptureArgs(1) { # $id = primary key of book to delete my ($self, $c, $id) = @_; - + # Find the book object and store it in the stash $c->stash(object => $c->stash->{resultset}->find($id)); - + # Make sure the lookup was successful. You would probably # want to do something like this in a real app: # $c->detach('/error_404') if !$c->stash->{object}; @@ -701,7 +701,7 @@ and add the following code: } Now, any other method that chains off C will automatically -have the appropriate book waiting for it in +have the appropriate book waiting for it in C<$c-Estash-E{object}>. Also note that we are using different technique for setting @@ -727,27 +727,27 @@ Open C in your editor and add the following method: =head2 delete - + Delete a book - + =cut - + sub delete :Chained('object') :PathPart('delete') :Args(0) { my ($self, $c) = @_; - + # Use the book object saved by 'object' and delete it along # with related 'book_authors' entries $c->stash->{object}->delete; - + # Set a status message to be displayed at the top of the view $c->stash->{status_msg} = "Book deleted."; - + # Forward to the list action/method in this controller $c->forward('list'); } -This method first deletes the book object saved by the C method. -However, it also removes the corresponding entry from the +This method first deletes the book object saved by the C method. +However, it also removes the corresponding entry from the C table with a cascading delete. Then, rather than forwarding to a "delete done" page as we did with the @@ -787,8 +787,8 @@ of the startup debug output: '-------------------------------------+--------------------------------------' Then point your browser to L and click -the "Delete" link next to the first "TCPIP_Illustrated_Vol-2". A green -"Book deleted" status message should display at the top of the page, +the "Delete" link next to the first "TCPIP_Illustrated_Vol-2". A green +"Book deleted" status message should display at the top of the page, along with a list of the eight remaining books. You will also see the cascading delete operation via the DBIC_TRACE output: @@ -800,15 +800,15 @@ cascading delete operation via the DBIC_TRACE output: =head2 Fixing a Dangerous URL -Note the URL in your browser once you have performed the deletion in the +Note the URL in your browser once you have performed the deletion in the prior step -- it is still referencing the delete action: http://localhost:3000/books/id/6/delete -What if the user were to press reload with this URL still active? In -this case the redundant delete is harmless (although it does generate -an exception screen, it doesn't perform any undesirable actions on the -application or database), but in other cases this could clearly be +What if the user were to press reload with this URL still active? In +this case the redundant delete is harmless (although it does generate +an exception screen, it doesn't perform any undesirable actions on the +application or database), but in other cases this could clearly be extremely dangerous. We can improve the logic by converting to a redirect. Unlike @@ -819,25 +819,25 @@ new request. As a result, the URL in the browser is updated to match the destination of the redirection URL. To convert the forward used in the previous section to a redirect, -open C and edit the existing +open C and edit the existing C method to match: =head2 delete - + Delete a book - + =cut - + sub delete :Chained('object') :PathPart('delete') :Args(0) { my ($self, $c) = @_; - + # Use the book object saved by 'object' and delete it along # with related 'book_authors' entries $c->stash->{object}->delete; - + # Set a status message to be displayed at the top of the view $c->stash->{status_msg} = "Book deleted."; - + # Redirect the user back to the list page. Note the use # of $self->action_for as earlier in this section (BasicCRUD) $c->response->redirect($c->uri_for($self->action_for('list'))); @@ -846,48 +846,48 @@ C method to match: =head2 Try the Delete and Redirect Logic -Restart the development server and point your browser to -L (don't just hit "Refresh" in your -browser since we left the URL in an invalid state in the previous -section!) and delete the first copy of the remaining two -"TCPIP_Illustrated_Vol-2" books. The URL in your browser should return -to the L URL, so that is an -improvement, but notice that I. Because the stash is reset on every request (and a redirect -involves a second request), the C is cleared before it can +Restart the development server and point your browser to +L (don't just hit "Refresh" in your +browser since we left the URL in an invalid state in the previous +section!) and delete the first copy of the remaining two +"TCPIP_Illustrated_Vol-2" books. The URL in your browser should return +to the L URL, so that is an +improvement, but notice that I. Because the stash is reset on every request (and a redirect +involves a second request), the C is cleared before it can be displayed. =head2 Using C to Pass Query Parameters -There are several ways to pass information across a redirect. One -option is to use the C technique that we will see in Part 5 of -the tutorial; however, here we will pass the information via query -parameters on the redirect itself. Open -C and update the existing C +There are several ways to pass information across a redirect. One +option is to use the C technique that we will see in Part 5 of +the tutorial; however, here we will pass the information via query +parameters on the redirect itself. Open +C and update the existing C method to match the following: - =head2 delete - + =head2 delete + Delete a book - + =cut - + sub delete :Chained('object') :PathPart('delete') :Args(0) { my ($self, $c) = @_; - + # Use the book object saved by 'object' and delete it along # with related 'book_authors' entries $c->stash->{object}->delete; - + # Redirect the user back to the list page with status msg as an arg - $c->response->redirect($c->uri_for($self->action_for('list'), + $c->response->redirect($c->uri_for($self->action_for('list'), {status_msg => "Book deleted."})); } This modification simply leverages the ability of C to include -an arbitrary number of name/value pairs in a hash reference. Next, we -need to update C to handle C as a +an arbitrary number of name/value pairs in a hash reference. Next, we +need to update C to handle C as a query parameter: ... @@ -900,60 +900,60 @@ query parameter: ... -Although the sample above only shows the C div, leave the +Although the sample above only shows the C div, leave the rest of the file intact -- the only change we made to the C -was to add "C<|| c.request.params.status_msg>" to the +was to add "C<|| c.request.params.status_msg>" to the Cspan class="message"E> line. =head2 Try the Delete and Redirect With Query Param Logic -Restart the development server and point your browser to -L (you should now be able to safely -hit "refresh" in your browser). Then delete the remaining copy of -"TCPIP_Illustrated_Vol-2". The green "Book deleted" status message +Restart the development server and point your browser to +L (you should now be able to safely +hit "refresh" in your browser). Then delete the remaining copy of +"TCPIP_Illustrated_Vol-2". The green "Book deleted" status message should return. -B Another popular method for maintaining server-side -information across a redirect is to use the C technique we -discuss in the next part of the tutorial, -L. While -C is a "slicker" mechanism in that it's all handled by the -server and doesn't "pollute" your URLs, B can lead to situations where the wrong information shows up -in the wrong browser window if the user has multiple windows or -browser tabs open.> For example, Window A causes something to be -placed in the stash, but before that window performs a redirect, -Window B makes a request to the server and gets the status information +B Another popular method for maintaining server-side +information across a redirect is to use the C technique we +discuss in the next part of the tutorial, +L. While +C is a "slicker" mechanism in that it's all handled by the +server and doesn't "pollute" your URLs, B can lead to situations where the wrong information shows up +in the wrong browser window if the user has multiple windows or +browser tabs open.> For example, Window A causes something to be +placed in the stash, but before that window performs a redirect, +Window B makes a request to the server and gets the status information that should really go to Window A. For this reason, you may wish to use the "query param" technique shown here in your applications. =head1 EXPLORING THE POWER OF DBIC -In this section we will explore some additional capabilities offered -by DBIx::Class. Although these features have relatively little to do -with Catalyst per se, you will almost certainly want to take advantage +In this section we will explore some additional capabilities offered +by DBIx::Class. Although these features have relatively little to do +with Catalyst per se, you will almost certainly want to take advantage of them in your applications. =head2 Convert to DBIC "load_namespaces" -If you look back at -L you will recall that we load our DBIC Result Classes -(Books.pm, Authors.pm and BookAuthors.pm) with in -C with the C feature. Although -this method is perfectly valid, the DBIC community has migrated to a -newer C technique because it more easily supports a -variety of advanced features. Since we want to explore some of these -features below, let's first migrate our configuration over to use +If you look back at +L you will recall that we load our DBIC Result Classes +(Books.pm, Authors.pm and BookAuthors.pm) with in +C with the C feature. Although +this method is perfectly valid, the DBIC community has migrated to a +newer C technique because it more easily supports a +variety of advanced features. Since we want to explore some of these +features below, let's first migrate our configuration over to use C. -If you are following along in Debian 5, you will need to upgrade your -version of -L to 0.23 -or higher. To do this, we can install directly from CPAN via the +If you are following along in Debian 5, you will need to upgrade your +version of +L to 0.23 +or higher. To do this, we can install directly from CPAN via the following command: $ sudo cpan Catalyst::Model::DBIC::Schema @@ -966,13 +966,13 @@ Then make sure you are running an appropriate version: Make sure you get version 0.23 or higher. -B Debian will automatically "do the right thing" and use the -module we installed from CPAN and ignore the older version we picked -up via the C command. If you are using a different -environment, you will need to make sure you are using v0.23 or higher +B Debian will automatically "do the right thing" and use the +module we installed from CPAN and ignore the older version we picked +up via the C command. If you are using a different +environment, you will need to make sure you are using v0.23 or higher with the command above. -While we are at it, let's install a few other modules from CPAN for +While we are at it, let's install a few other modules from CPAN for some of the other work we will be doing below: $ cpan Time::Warp DBICx::TestDatabase \ @@ -996,52 +996,52 @@ the model and schema information: $ ls lib/MyApp/Schema/Result Authors.pm BookAuthors.pm Books.pm -Notice that we now have a duplicate set of Result Class files. With -the newer C feature, DBIC automatically looks for -your Result Class files in a subdirectory of the Schema directory -called C (the files in C were already there -from Part 3 of the tutorial; the files in C -are new). +Notice that we now have a duplicate set of Result Class files. With +the newer C feature, DBIC automatically looks for +your Result Class files in a subdirectory of the Schema directory +called C (the files in C were already there +from Part 3 of the tutorial; the files in C +are new). -If you are using SQLite, you will need to manually re-enter the -relationship configuration as we did in Part 3 of the tutorial (if you +If you are using SQLite, you will need to manually re-enter the +relationship configuration as we did in Part 3 of the tutorial (if you are using different database, the relationships might have been auto- -generated by Schema::Loader). One option is to use the following -command-line perl script to migrate the information across +generated by Schema::Loader). One option is to use the following +command-line perl script to migrate the information across automatically: $ cd lib/MyApp/Schema - $ perl -MIO::All -e 'for (@ARGV) { my $s < io($_); $s =~ s/.*\n\# You can replace.*?\n//s; - $s =~ s/'MyApp::Schema::/'MyApp::Schema::Result::/g; my $d < io("Result/$_"); + $ perl -MIO::All -e 'for (@ARGV) { my $s < io($_); $s =~ s/.*\n\# You can replace.*?\n//s; + $s =~ s/'MyApp::Schema::/'MyApp::Schema::Result::/g; my $d < io("Result/$_"); $d =~ s/1;\n?//; "$d$s" > io("Result/$_"); }' *.pm $ cd ../../.. -If you prefer, you can do the migration by hand using "cut and paste" -from the files in C (or from -L) -to the corresponding files in C. If you take -this approach, be sure to add C<::Result> to the end of -C in all three files (for example, in C, the -"peer class" in the C relationship needs to be changed from +If you prefer, you can do the migration by hand using "cut and paste" +from the files in C (or from +L) +to the corresponding files in C. If you take +this approach, be sure to add C<::Result> to the end of +C in all three files (for example, in C, the +"peer class" in the C relationship needs to be changed from C to C). -Now we can remove the original set of Result Class files that we no +Now we can remove the original set of Result Class files that we no longer need: $ rm lib/MyApp/Schema/*.pm $ ls lib/MyApp/Schema Result -Finally, test the application to be sure everything is still -working under our new configuration. Use the -C