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