In keeping with the Catalyst (and Perl) spirit of flexibility, there are
many different ways approach advanced CRUD operations in a Catalyst
environment. One alternative is to use
-L<Catalyst::Helper::Controller::Scaffold> 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<Data::FormValidator> and
-L<HTML::FillInForm>.
-
-Here, we will make use of the L<HTML::Widget> 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<HTML::Widget> 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<Catalyst::Helper::Controller::Scaffold|Catalyst::Helper::Controller::Scaffold>
+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<Data::FormValidator|Data::FormValidator> and
+L<HTML::FillInForm|HTML::FillInForm>.
+
+Here, we will make use of the L<HTML::Widget|HTML::Widget> 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<HTML::Widget|HTML::Widget> 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<Note:> Part 8 of the tutorial is optional. Users who do not wish to
-use L<HTML::Widget|HTML::Widget> may skip this section.
+use L<HTML::Widget|HTML::Widget> may skip this part.
B<TIP>: 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
=head1 C<HTML::WIDGET> FORM CREATION
-This section looks at how L<HTML::Widget> can be used to
+This section looks at how L<HTML::Widget|HTML::Widget> can be used to
add additional functionality to the manually created form from Part 3.
=head2 Add the C<HTML::Widget> Plugin
sub make_book_widget {
my ($self, $c) = @_;
-
+
# Create an HTML::Widget to build the form
my $w = $c->widget('book_form')->method('post');
$w->element('Select', 'authors')->label('Authors')
->options(@authors);
$w->element('Submit', 'submit' )->value('submit');
-
+
# Return the widget
return $w;
}
=head2 Create a Template Page To Display The Form
-C<root/src/books/hw_form.tt2>
+Open C<root/src/books/hw_form.tt2> in your editor and enter the following:
+
[% META title = 'Create/Update Book' %]
[% widget_result.as_xml %]
sub make_book_widget {
my ($self, $c) = @_;
-
+
# Create an HTML::Widget to build the form
my $w = $c->widget('book_form')->method('post');
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);
Kennedy Clark, C<hkclark@gmail.com>
-Please report any errors, issues or suggestions to the author.
+Please report any errors, issues or suggestions to the author. The
+most recent version of the Catlayst Tutorial can be found at
+L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
Copyright 2006, Kennedy Clark, under Creative Commons License
(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
actions. Remember, Catalyst is designed to be very flexible, and leaves
such matters up to you, the designer and programmer.
-Then open C<lib/MyApp/Controller/Login.pm> and add:
+Then open C<lib/MyApp/Controller/Login.pm>, locate the C<sub index :
+Private> method (this was automatically inserted by the helpers when we
+created the Login controller above), and delete this line:
+
+ $c->response->body('Matched MyApp::Controller::Login in Login.');
+
+Then update it to match:
=head2 base
=cut
- sub base :Path :Args(0) {
+ sub index : Private {
my ($self, $c) = @_;
# Get the username and password from form
C<password> values are not present in the form, the user will be taken
to the empty login form.
-We are using C<sub base :Path :Args(0) {...}> here to specifically match
-the URL C</login>. C<Path> actions (aka, "literal actions") create URI
-matches relative to the namespace of the controller where they are defined.
-Although C<Path> supports arguments that allow relative and absolute paths
-to be defined, here we use an empty C<Path> definition to match on just the
-name of the controller itself. The method name, C<base>, is arbitrary.
-We make the match even more specific with the C<:Args(0)> action modifier
--- this forces the match on I<only> C</login>, not C</login/somethingelse>.
-
Note that we could have used something like C<sub default :Private>;
however, the use of C<default> actions is discouraged because it does
not receive path args as with other actions. The recommended practice
is to only use C<default> in C<MyApp::Controller::Root>.
+Another options would be to use something like
+C<sub base :Path :Args(0) {...}> (where the C<...> refers to the login
+code shown in C<sub index : Private> above). We are using C<sub base
+:Path :Args(0) {...}> here to specifically match the URL C</login>.
+C<Path> actions (aka, "literal actions") create URI matches relative to
+the namespace of the controller where they are defined. Although
+C<Path> supports arguments that allow relative and absolute paths to be
+defined, here we use an empty C<Path> definition to match on just the
+name of the controller itself. The method name, C<base>, is arbitrary.
+We make the match even more specific with the C<:Args(0)> action
+modifier -- this forces the match on I<only> C</login>, not
+C</login/somethingelse>.
+
Next, create a corresponding method in C<lib/MyApp/Controller/Logout.pm>:
=head2 base
=cut
- sub base :Path :Args(0) {
+ sub index : Private {
my ($self, $c) = @_;
# Clear the user's state
$c->response->redirect($c->uri_for('/'));
}
-Note that we are using the same C<sub base :Path :Args(0) {...}> style
-of action as with the login logic.
+As with the login controller, be sure to delete the
+C<$c->response->body('Matched MyApp::Controller::Logout in Logout.');>
+line of the C<sub index>.
=head2 Add a Login Form TT Template Page
<a href="[% Catalyst.uri_for('form_create') %]">Create</a>
</p>
-Reload your browser and you should now see a "Login" and "Create" links
-at the bottom of the page (as mentioned earlier, you can update
-template files without reloading the development server). Click this
-link to return to the login page. This time you I<should> see the
-"You are already logged in" message.
+Reload your browser and you should now see a "Login" and "Create" links
+at the bottom of the page (as mentioned earlier, you can update template
+files without reloading the development server). Click the first link
+to return to the login page. This time you I<should> see the "You are
+already logged in" message.
Finally, click the C<You can logout here> link on the C</login> page.
You should stay at the login page, but the message should change to "You
login as before. When done, click the "Logout" link on the login page
(or point your browser at L<http://localhost:3000/logout>).
+B<Note:> If you receive the debug screen in your browser with a
+C<Can't call method "stash" on an undefined value...> error message,
+make sure that you are using v0.07 of
+L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>.
+The following command can be a useful way to quickly dump the version number
+of this module on your system:
+
+ perl -MCatalyst::Plugin::Authorization::ACL -e 'print $Catalyst::Plugin::Authorization::ACL::VERSION, "\n";'
+
=head1 AUTHOR
# all tests. Better to replace "'no_plan'" with "tests => 30" so it
# knows exactly how many tests need to be run (and will tell you if
# not), but 'no_plan' is nice for quick & dirty tests
-
+
use Test::More 'no_plan';
# Need to specify the name of your app as arg on next line
# Can also do:
# use Test::WWW::Mechanize::Catalyst "MyApp";
-
+
use ok "Test::WWW::Mechanize::Catalyst" => "MyApp";
# Create two 'user agents' to simulate two different users ('test01' & 'test02')
$ua1->get_ok("http://localhost/books/url_create/TestTitle/2/4",
"'test01' formless create");
$ua1->title_is("Book Created", "Book created title");
- $ua1->content_contains("Added book 'TestTitle' by 'Stevens'", "Check added OK");
+ $ua1->content_contains("Added book 'TestTitle'", "Check title added OK");
+ $ua1->content_contains("by 'Stevens'", "Check author added OK");
$ua1->content_contains("a rating of 2.", "Check rating added");
# Make sure the new book shows in the list