Doc tweaks from drewbie
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
CommitLineData
fc7ec1d9 1=head1 NAME
2
3Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5=head1 DESCRIPTION
6
aba94964 7Yummy code like your mum used to bake!
fc7ec1d9 8
9=head1 RECIPES
10
11=head2 Force debug screen
12
eff5f524 13You can force Catalyst to display the debug screen at the end of the request by
14placing a C<die()> call in the C<end> action.
fc7ec1d9 15
61b1e958 16 sub end : Private {
17 my ( $self, $c ) = @_;
2343e117 18 die "forced debug";
61b1e958 19 }
fc7ec1d9 20
aff93052 21If you're tired of removing and adding this all the time, you
eff5f524 22can easily add a condition. For example:
aff93052 23
eff5f524 24 die "force debug" if $c->req->params->{dump_info};
aff93052 25
fc7ec1d9 26=head2 Disable statistics
27
28Just add this line to your application class if you don't want those nifty
29statistics in your debug messages.
30
31 sub Catalyst::Log::info { }
32
33=head2 Scaffolding
34
35Scaffolding is very simple with Catalyst.
51ef2818 36Just use Catalyst::Model::CDBI::CRUD as your base class.
fc7ec1d9 37
38 # lib/MyApp/Model/CDBI.pm
39 package MyApp::Model::CDBI;
40
41 use strict;
42 use base 'Catalyst::Model::CDBI::CRUD';
43
44 __PACKAGE__->config(
45 dsn => 'dbi:SQLite:/tmp/myapp.db',
46 relationships => 1
47 );
48
49 1;
50
51 # lib/MyApp.pm
52 package MyApp;
53
54 use Catalyst 'FormValidator';
55
56 __PACKAGE__->config(
57 name => 'My Application',
58 root => '/home/joeuser/myapp/root'
59 );
60
61b1e958 61 sub my_table : Global {
62 my ( $self, $c ) = @_;
63 $c->form( optional => [ MyApp::Model::CDBI::Table->columns ] );
64 $c->forward('MyApp::Model::CDBI::Table');
65 }
fc7ec1d9 66
67 1;
68
eff5f524 69Modify the $c->form() parameters to match your needs, and don't forget to copy
70the templates into the template root. Can't find the templates? They were in the
71CRUD model distribution, so you can do B<look Catalyst::Model::CDBI::CRUD> from
72the CPAN shell to find them.
fc7ec1d9 73
5c0ff128 74=head2 Single file upload with Catalyst
aba94964 75
76To implement uploads in Catalyst you need to have a HTML form similiar to
77this:
78
79 <form action="/upload" method="post" enctype="multipart/form-data">
80 <input type="hidden" name="form_submit" value="yes">
81 <input type="file" name="my_file">
82 <input type="submit" value="Send">
83 </form>
84
eff5f524 85It's very important not to forget C<enctype="multipart/form-data"> in form. Uploads will not work without this.
aba94964 86
87Catalyst Controller module 'upload' action:
88
5c0ff128 89 sub upload : Global {
90 my ($self, $c) = @_;
4d89569d 91
92 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
93
94 if ( my $upload = $c->request->upload('my_file') ) {
47ae6960 95
5c0ff128 96 my $filename = $upload->filename;
47ae6960 97 my $target = "/tmp/upload/$filename";
98
3ffaf022 99 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 100 die( "Failed to copy '$filename' to '$target': $!" );
5c0ff128 101 }
5c0ff128 102 }
103 }
4d89569d 104
5c0ff128 105 $c->stash->{template} = 'file_upload.html';
106 }
107
108=head2 Multiple file upload with Catalyst
109
eff5f524 110Code for uploading multiple files from one form needs little changes compared
111to single file upload.
5c0ff128 112
eff5f524 113Form goes like this:
5c0ff128 114
115 <form action="/upload" method="post" enctype="multipart/form-data">
116 <input type="hidden" name="form_submit" value="yes">
117 <input type="file" name="file1" size="50"><br>
118 <input type="file" name="file2" size="50"><br>
119 <input type="file" name="file3" size="50"><br>
120 <input type="submit" value="Send">
121 </form>
122
eff5f524 123Controller:
5c0ff128 124
125 sub upload : Local {
126 my ($self, $c) = @_;
4d89569d 127
128 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
129
130 for my $field ( $c->req->upload ) {
131
02a53b81 132 my $upload = $c->req->upload($field);
4d89569d 133 my $filename = $upload->filename;
47ae6960 134 my $target = "/tmp/upload/$filename";
135
3ffaf022 136 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 137 die( "Failed to copy '$filename' to '$target': $!" );
aba94964 138 }
139 }
61b1e958 140 }
4d89569d 141
5c0ff128 142 $c->stash->{template} = 'file_upload.html';
143 }
144
eff5f524 145C<for my $field ($c-E<gt>req->upload)> loops automatically over all file input
146fields and gets input names. After that is basic file saving code, just like in
147single file upload.
aba94964 148
eff5f524 149Notice: C<die>ing might not be what you want to do, when an error occurs, but
150it works as an example. A better idea would be to store error C<$!> in
151$c->stash->{error} and show a custom error template displaying this message.
aba94964 152
5c0ff128 153For more information about uploads and usable methods look at
eff5f524 154C<Catalyst::Request::Upload> and C<Catalyst::Request>.
aba94964 155
deb90705 156=head2 Authentication with Catalyst::Plugin::Authentication::CDBI
157
158There are (at least) two ways to implement authentication with this plugin:
eff5f524 1591) only checking username and password;
1602) checking username, password and the roles the user has
deb90705 161
162For both variants you'll need the following code in your MyApp package:
163
164 use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
165
166 MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
167 user_field => 'email',
168 password_field => 'password' });
169
170'user_class' is a Class::DBI class for your users table.
171'user_field' tells which field is used for username lookup (might be
51ef2818 172email, first name, surname etc.).
deb90705 173'password_field' is, well, password field in your table and by default
174password is stored in plain text. Authentication::CDBI looks for 'user'
175and 'password' fields in table, if they're not defined in the config.
176
51ef2818 177In PostgreSQL, the users table might be something like:
deb90705 178
51ef2818 179 CREATE TABLE users (
180 user_id serial,
181 name varchar(100),
182 surname varchar(100),
183 password varchar(100),
184 email varchar(100),
185 primary key(user_id)
186 );
deb90705 187
188We'll discuss the first variant for now:
51ef2818 1891. user:password login/auth without roles
deb90705 190
51ef2818 191To log in a user you might use an action like this:
deb90705 192
7c1078a4 193 sub login : Local {
deb90705 194 my ($self, $c) = @_;
195 if ($c->req->params->{username}) {
196 $c->session_login($c->req->params->{username},
61b1e958 197 $c->req->params->{password} );
deb90705 198 if ($c->req->{user}) {
199 $c->forward('?restricted_area');
200 }
201 }
61b1e958 202 }
deb90705 203
7c1078a4 204This action should not go in your MyApp class...if it does, it will
205conflict with the built-in method of the same name. Instead, put it
206in a Controller class.
207
deb90705 208$c->req->params->{username} and $c->req->params->{password} are html
61b1e958 209form parameters from a login form. If login succeeds, then
210$c->req->{user} contains the username of the authenticated user.
deb90705 211
51ef2818 212If you want to remember the user's login status in between further
213requests, then just use the C<$c-E<gt>session_login> method. Catalyst will
214create a session id and session cookie and automatically append session
215id to all urls. So all you have to do is just check $c->req->{user}
61b1e958 216where needed.
deb90705 217
51ef2818 218To log out a user, just call $c->session_logout.
deb90705 219
51ef2818 220Now let's take a look at the second variant:
2212. user:password login/auth with roles
deb90705 222
51ef2818 223To use roles you need to add the following parameters to MyApp->config in the 'authentication' section:
deb90705 224
225 role_class => 'MyApp::M::MyApp::Roles',
226 user_role_class => 'MyApp::M::MyApp::UserRoles',
227 user_role_user_field => 'user_id',
228 user_role_role_field => 'role_id',
229
230Corresponding tables in PostgreSQL could look like this:
231
51ef2818 232 CREATE TABLE roles (
233 role_id serial,
234 name varchar(100),
235 primary key(role_id)
236 );
237
238 CREATE TABLE user_roles (
239 user_role_id serial,
240 user_id int,
241 role_id int,
242 primary key(user_role_id),
243 foreign key(user_id) references users(user_id),
244 foreign key(role_id) references roles(role_id)
245 );
deb90705 246
61b1e958 247The 'roles' table is a list of role names and the 'user_role' table is
248used for the user -> role lookup.
deb90705 249
51ef2818 250Now if a logged-in user wants to see a location which is allowed only
251for people with an 'admin' role, in your controller you can check it
61b1e958 252with:
deb90705 253
61b1e958 254 sub add : Local {
deb90705 255 my ($self, $c) = @_;
256 if ($c->roles(qw/admin/)) {
257 $c->req->output("Your account has the role 'admin.'");
258 } else {
51ef2818 259 $c->req->output("You're not allowed to be here.");
deb90705 260 }
61b1e958 261 }
deb90705 262
51ef2818 263One thing you might need is to forward non-authenticated users to a login
264form if they try to access restricted areas. If you want to do this
265controller-wide (if you have one controller for your admin section) then it's
266best to add a user check to a '!begin' action:
deb90705 267
61b1e958 268 sub begin : Private {
deb90705 269 my ($self, $c) = @_;
270 unless ($c->req->{user}) {
271 $c->req->action(undef); ## notice this!!
272 $c->forward('?login');
273 }
61b1e958 274 }
deb90705 275
51ef2818 276Pay attention to $c->req->action(undef). This is needed because of the
277way $c->forward works - C<forward> to C<login> gets called, but after that
278Catalyst will still execute the action defined in the URI (e.g. if you
279tried to go to C</add>, then first 'begin' will forward to 'login', but after
280that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any
281actions that were to be called and forwards the user where we want him/her
deb90705 282to be.
283
51ef2818 284And this is all you need to do.
deb90705 285
afb208ae 286=head2 Pass-through login (and other actions)
287
eff5f524 288An easy way of having assorted actions that occur during the processing
289of a request that are orthogonal to its actual purpose - logins, silent
afb208ae 290commands etc. Provide actions for these, but when they're required for
eff5f524 291something else fill e.g. a form variable __login and have a sub begin
292like so:
afb208ae 293
eff5f524 294 sub begin : Private {
295 my ($self, $c) = @_;
296 foreach my $action (qw/login docommand foo bar whatever/) {
297 if ($c->req->params->{"__${action}"}) {
298 $c->forward($action);
299 }
300 }
afb208ae 301 }
145074c2 302
303=head2 How to use Catalyst without mod_perl
304
305Catalyst applications give optimum performance when run under mod_perl.
61b1e958 306However sometimes mod_perl is not an option, and running under CGI is
51ef2818 307just too slow. There's also an alternative to mod_perl that gives
dec2a2a9 308reasonable performance named FastCGI.
145074c2 309
310B<Using FastCGI>
311
61b1e958 312To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
313independent, scalable, extension to CGI that provides high performance
314without the limitations of specific server APIs." Web server support
315is provided for Apache in the form of C<mod_fastcgi> and there is Perl
316support in the C<FCGI> module. To convert a CGI Catalyst application
317to FastCGI one needs to initialize an C<FCGI::Request> object and loop
318while the C<Accept> method returns zero. The following code shows how
319it is done - and it also works as a normal, single-shot CGI script.
145074c2 320
321 #!/usr/bin/perl
322 use strict;
323 use FCGI;
324 use MyApp;
325
326 my $request = FCGI::Request();
327 while ($request->Accept() >= 0) {
1c61c726 328 MyApp->run;
145074c2 329 }
330
61b1e958 331Any initialization code should be included outside the request-accept
332loop.
145074c2 333
51ef2818 334There is one little complication, which is that C<MyApp-E<gt>run> outputs a
61b1e958 335complete HTTP response including the status line (e.g.:
336"C<HTTP/1.1 200>").
337FastCGI just wants a set of headers, so the sample code captures the
338output and drops the first line if it is an HTTP status line (note:
339this may change).
340
341The Apache C<mod_fastcgi> module is provided by a number of Linux
342distros and is straightforward to compile for most Unix-like systems.
343The module provides a FastCGI Process Manager, which manages FastCGI
344scripts. You configure your script as a FastCGI script with the
345following Apache configuration directives:
145074c2 346
347 <Location /fcgi-bin>
348 AddHandler fastcgi-script fcgi
349 </Location>
350
351or:
352
353 <Location /fcgi-bin>
354 SetHandler fastcgi-script
355 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
356 </Location>
357
358C<mod_fastcgi> provides a number of options for controlling the FastCGI
359scripts spawned; it also allows scripts to be run to handle the
51ef2818 360authentication, authorization, and access check phases.
145074c2 361
61b1e958 362For more information see the FastCGI documentation, the C<FCGI> module
363and L<http://www.fastcgi.com/>.
eff5f524 364
365=head2 Forwarding with a parameter
145074c2 366
eff5f524 367Sometimes you want to pass along arguments when forwarding to another
368action. As of version 5.30, arguments can be passed in the call to
369C<forward>; in earlier versions, you can manually set the arguments in
370the Catalyst Request object:
e6394847 371
eff5f524 372 # version 5.30 and later:
373 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
2343e117 374
eff5f524 375 # pre-5.30
2343e117 376 $c->req->args([qw/arg1 arg2 arg3/]);
377 $c->forward('/wherever');
378
eff5f524 379(See L<Catalyst::Manual::Intro#Flow_Control> for more information on
380passing arguments via C<forward>.)
381
fc7ec1d9 382=head1 AUTHOR
383
384Sebastian Riedel, C<sri@oook.de>
eff5f524 385Danijel Milicevic C<me@danijel.de>
386Viljo Marrandi C<vilts@yahoo.com>
387Marcus Ramberg C<mramberg@cpan.org>
fc7ec1d9 388
389=head1 COPYRIGHT
390
61b1e958 391This program is free software, you can redistribute it and/or modify it
392under the same terms as Perl itself.