revised angry smiley.
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
1 =head1 NAME
2
3 Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5 =head1 DESCRIPTION
6
7 Yummy code like your mum used to bake!
8
9 =head1 RECIPES
10
11 =head2 Force debug screen
12
13 You can force Catalyst to display the debug screen at the end of the request by
14 placing a C<die()> call in the C<end> action.
15
16      sub end : Private {
17          my ( $self, $c ) = @_;
18          die "forced debug";
19      }
20
21 If you're tired of removing and adding this all the time, you
22 can easily add a condition. For example:
23
24   die "force debug" if $c->req->params->{dump_info};
25
26 =head2 Disable statistics
27
28 Just add this line to your application class if you don't want those nifty
29 statistics in your debug messages.
30
31     sub Catalyst::Log::info { }
32
33 =head2 Scaffolding
34
35 Scaffolding is very simple with Catalyst.
36 Just use Catalyst::Model::CDBI::CRUD as your base class.
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
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     }
66
67     1;
68
69 Modify the $c->form() parameters to match your needs, and don't forget to copy
70 the templates into the template root. Can't find the templates? They were in the
71 CRUD model distribution, so you can do B<look Catalyst::Model::CDBI::CRUD> from 
72 the CPAN shell to find them.
73
74 =head2 Single file upload with Catalyst
75
76 To implement uploads in Catalyst you need to have a HTML form similiar to
77 this:
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
85 It's very important not to forget C<enctype="multipart/form-data"> in form. Uploads will not work without this.
86
87 Catalyst Controller module 'upload' action:
88
89     sub upload : Global {
90         my ($self, $c) = @_;
91
92         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
93
94             if ( my $upload = $c->request->upload('my_file') ) {
95             
96                 my $filename = $upload->filename;
97                 my $target   = "/tmp/upload/$filename";
98                 
99                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
100                     die( "Failed to copy '$filename' to '$target': $!" );
101                 }
102             }
103         }
104         
105         $c->stash->{template} = 'file_upload.html';
106     }
107
108 =head2 Multiple file upload with Catalyst
109
110 Code for uploading multiple files from one form needs little changes compared
111 to single file upload.
112
113 Form goes like this:
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
123 Controller:
124
125     sub upload : Local {
126         my ($self, $c) = @_;
127
128         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
129
130             for my $field ( $c->req->upload ) {
131
132                 my $upload   = $c->req->upload($field);
133                 my $filename = $upload->filename;
134                 my $target   = "/tmp/upload/$filename";
135                 
136                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
137                     die( "Failed to copy '$filename' to '$target': $!" );
138                 }
139             }
140         }
141
142         $c->stash->{template} = 'file_upload.html';
143     }
144
145 C<for my $field ($c-E<gt>req->upload)> loops automatically over all file input
146 fields and gets input names. After that is basic file saving code, just like in
147 single file upload.
148
149 Notice: C<die>ing might not be what you want to do, when an error occurs, but
150 it 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.
152
153 For more information about uploads and usable methods look at
154 C<Catalyst::Request::Upload> and C<Catalyst::Request>.
155
156 =head2 Authentication with Catalyst::Plugin::Authentication::CDBI
157
158 There are (at least) two ways to implement authentication with this plugin:
159 1) only checking username and password;
160 2) checking username, password and the roles the user has
161
162 For 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 
172 email, first name, surname etc.).
173 'password_field' is, well, password field in your table and by default 
174 password is stored in plain text. Authentication::CDBI looks for 'user' 
175 and 'password' fields in table, if they're not defined in the config.
176
177 In PostgreSQL, the users table might be something like:
178
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  );
187
188 We'll discuss the first variant for now:
189 1. user:password login/auth without roles
190
191 To log in a user you might use an action like this:
192
193     sub login : Local {
194         my ($self, $c) = @_;
195         if ($c->req->params->{username}) {
196             $c->session_login($c->req->params->{username}, 
197                               $c->req->params->{password} );
198             if ($c->req->{user}) {
199                 $c->forward('?restricted_area');
200             }
201         }
202     }
203
204 This action should not go in your MyApp class...if it does, it will
205 conflict with the built-in method of the same name.  Instead, put it
206 in a Controller class.
207
208 $c->req->params->{username} and $c->req->params->{password} are html 
209 form parameters from a login form. If login succeeds, then 
210 $c->req->{user} contains the username of the authenticated user.
211
212 If you want to remember the user's login status in between further 
213 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will 
214 create a session id and session cookie and automatically append session 
215 id to all urls. So all you have to do is just check $c->req->{user} 
216 where needed.
217
218 To log out a user, just call $c->session_logout.
219
220 Now let's take a look at the second variant:
221 2. user:password login/auth with roles
222
223 To use roles you need to add the following parameters to  MyApp->config in the 'authentication' section:
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
230 Corresponding tables in PostgreSQL could look like this:
231
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  );
246
247 The 'roles' table is a list of role names and the 'user_role' table is 
248 used for the user -> role lookup.
249
250 Now if a logged-in user wants to see a location which is allowed only 
251 for people with an 'admin' role, in your controller you can check it 
252 with:
253
254     sub add : Local {
255         my ($self, $c) = @_;
256         if ($c->roles(qw/admin/)) {
257             $c->req->output("Your account has the role 'admin.'");
258         } else {
259             $c->req->output("You're not allowed to be here.");
260         }
261     }
262
263 One thing you might need is to forward non-authenticated users to a login 
264 form if they try to access restricted areas. If you want to do this 
265 controller-wide (if you have one controller for your admin section) then it's 
266 best to add a user check to a '!begin' action:
267
268     sub begin : Private {
269         my ($self, $c) = @_;
270         unless ($c->req->{user}) {
271             $c->req->action(undef);  ## notice this!!
272             $c->forward('?login');
273         }
274     }
275
276 Pay attention to $c->req->action(undef). This is needed because of the 
277 way $c->forward works - C<forward> to C<login> gets called, but after that 
278 Catalyst will still execute the action defined in the URI (e.g. if you 
279 tried to go to C</add>, then first 'begin' will forward to 'login', but after
280 that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any 
281 actions that were to be called and forwards the user where we want him/her 
282 to be.
283
284 And this is all you need to do. 
285
286 =head2 Pass-through login (and other actions)
287
288 An easy way of having assorted actions that occur during the processing of
289 a request that are orthogonal to its actual purpose - logins, silent
290 commands etc. Provide actions for these, but when they're required for
291 something else fill e.g. a form variable __login and have a sub begin like so:
292
293 sub begin : Private {
294   my ($self, $c) = @_;
295   foreach my $action (qw/login docommand foo bar whatever/) {
296     if ($c->req->params->{"__${action}"}) {
297       $c->forward($action);
298     }
299   }
300 }
301
302 =head2 How to use Catalyst without mod_perl
303
304 Catalyst applications give optimum performance when run under mod_perl.
305 However sometimes mod_perl is not an option, and running under CGI is 
306 just too slow.  There's also an alternative to mod_perl that gives
307 reasonable performance named FastCGI.
308
309 B<Using FastCGI>
310
311 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
312 independent, scalable, extension to CGI that provides high performance 
313 without the limitations of specific server APIs."  Web server support 
314 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
315 support in the C<FCGI> module.  To convert a CGI Catalyst application 
316 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
317 while the C<Accept> method returns zero.  The following code shows how 
318 it is done - and it also works as a normal, single-shot CGI script.
319
320     #!/usr/bin/perl
321     use strict;
322     use FCGI;
323     use MyApp;
324
325     my $request = FCGI::Request();
326     while ($request->Accept() >= 0) {
327         MyApp->run;
328     }
329
330 Any initialization code should be included outside the request-accept 
331 loop.
332
333 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
334 complete HTTP response including the status line (e.g.: 
335 "C<HTTP/1.1 200>").
336 FastCGI just wants a set of headers, so the sample code captures the 
337 output and  drops the first line if it is an HTTP status line (note: 
338 this may change).
339
340 The Apache C<mod_fastcgi> module is provided by a number of Linux 
341 distros and is straightforward to compile for most Unix-like systems.  
342 The module provides a FastCGI Process Manager, which manages FastCGI 
343 scripts.  You configure your script as a FastCGI script with the 
344 following Apache configuration directives:
345
346     <Location /fcgi-bin>
347        AddHandler fastcgi-script fcgi
348     </Location>
349
350 or:
351
352     <Location /fcgi-bin>
353        SetHandler fastcgi-script
354        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
355     </Location>
356
357 C<mod_fastcgi> provides a number of options for controlling the FastCGI
358 scripts spawned; it also allows scripts to be run to handle the
359 authentication, authorization, and access check phases.
360
361 For more information see the FastCGI documentation, the C<FCGI> module 
362 and L<http://www.fastcgi.com/>.
363
364 =head1 Forwarding with a parameter
365
366 Sometimes you want to pass along arguments when forwarding to another
367 action. This can easily be accomplished like this:
368  
369   $c->req->args([qw/arg1 arg2 arg3/]);
370   $c->forward('/wherever');
371
372 =head1 AUTHOR
373
374 Sebastian Riedel, C<sri@oook.de>
375 Danijel Milicevic C<me@danijel.de>
376 Viljo Marrandi C<vilts@yahoo.com>
377 Marcus Ramberg C<mramberg@cpan.org>
378
379 =head1 COPYRIGHT
380
381 This program is free software, you can redistribute it and/or modify it
382 under the same terms as Perl itself.