Fixed cookbook
[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 "testing";
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 "Testing" if $c->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 $c->req->params->{username} and $c->req->params->{password} are html 
203 form parameters from a login form. If login succeeds, then 
204 $c->req->{user} contains the username of the authenticated user.
205
206 If you want to remember the user's login status in between further 
207 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will 
208 create a session id and session cookie and automatically append session 
209 id to all urls. So all you have to do is just check $c->req->{user} 
210 where needed.
211
212 To log out a user, just call $c->session_logout.
213
214 Now let's take a look at the second variant:
215 2. user:password login/auth with roles
216
217 To use roles you need to add the following parameters to  MyApp->config in the 'authentication' section:
218
219     role_class      => 'MyApp::M::MyApp::Roles',
220     user_role_class => 'MyApp::M::MyApp::UserRoles',
221     user_role_user_field => 'user_id',
222     user_role_role_field => 'role_id',
223
224 Corresponding tables in PostgreSQL could look like this:
225
226  CREATE TABLE roles (
227    role_id  serial,
228    name     varchar(100),
229    primary key(role_id)
230  );
231
232  CREATE TABLE user_roles (
233    user_role_id  serial,
234    user_id       int,
235    role_id       int,
236    primary key(user_role_id),
237    foreign key(user_id) references users(user_id),
238    foreign key(role_id) references roles(role_id)
239  );
240
241 The 'roles' table is a list of role names and the 'user_role' table is 
242 used for the user -> role lookup.
243
244 Now if a logged-in user wants to see a location which is allowed only 
245 for people with an 'admin' role, in your controller you can check it 
246 with:
247
248     sub add : Local {
249         my ($self, $c) = @_;
250         if ($c->roles(qw/admin/)) {
251             $c->req->output("Your account has the role 'admin.'");
252         } else {
253             $c->req->output("You're not allowed to be here.");
254         }
255     }
256
257 One thing you might need is to forward non-authenticated users to a login 
258 form if they try to access restricted areas. If you want to do this 
259 controller-wide (if you have one controller for your admin section) then it's 
260 best to add a user check to a '!begin' action:
261
262     sub begin : Private {
263         my ($self, $c) = @_;
264         unless ($c->req->{user}) {
265             $c->req->action(undef);  ## notice this!!
266             $c->forward('?login');
267         }
268     }
269
270 Pay attention to $c->req->action(undef). This is needed because of the 
271 way $c->forward works - C<forward> to C<login> gets called, but after that 
272 Catalyst will still execute the action defined in the URI (e.g. if you 
273 tried to go to C</add>, then first 'begin' will forward to 'login', but after
274 that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any 
275 actions that were to be called and forwards the user where we want him/her 
276 to be.
277
278 And this is all you need to do. 
279
280
281 =head2 How to use Catalyst without mod_perl
282
283 Catalyst applications give optimum performance when run under mod_perl.
284 However sometimes mod_perl is not an option, and running under CGI is 
285 just too slow.  There's also an alternative to mod_perl that gives
286 reasonable performance named FastCGI.
287
288 B<Using FastCGI>
289
290 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
291 independent, scalable, extension to CGI that provides high performance 
292 without the limitations of specific server APIs."  Web server support 
293 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
294 support in the C<FCGI> module.  To convert a CGI Catalyst application 
295 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
296 while the C<Accept> method returns zero.  The following code shows how 
297 it is done - and it also works as a normal, single-shot CGI script.
298
299     #!/usr/bin/perl
300     use strict;
301     use FCGI;
302     use MyApp;
303
304     my $request = FCGI::Request();
305     while ($request->Accept() >= 0) {
306         MyApp->run;
307     }
308
309 Any initialization code should be included outside the request-accept 
310 loop.
311
312 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
313 complete HTTP response including the status line (e.g.: 
314 "C<HTTP/1.1 200>").
315 FastCGI just wants a set of headers, so the sample code captures the 
316 output and  drops the first line if it is an HTTP status line (note: 
317 this may change).
318
319 The Apache C<mod_fastcgi> module is provided by a number of Linux 
320 distros and is straightforward to compile for most Unix-like systems.  
321 The module provides a FastCGI Process Manager, which manages FastCGI 
322 scripts.  You configure your script as a FastCGI script with the 
323 following Apache configuration directives:
324
325     <Location /fcgi-bin>
326        AddHandler fastcgi-script fcgi
327     </Location>
328
329 or:
330
331     <Location /fcgi-bin>
332        SetHandler fastcgi-script
333        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
334     </Location>
335
336 C<mod_fastcgi> provides a number of options for controlling the FastCGI
337 scripts spawned; it also allows scripts to be run to handle the
338 authentication, authorization, and access check phases.
339
340 For more information see the FastCGI documentation, the C<FCGI> module 
341 and L<http://www.fastcgi.com/>.
342
343 =head1 AUTHOR
344
345 Sebastian Riedel, C<sri@oook.de>
346 Danijel Milicevic C<me@danijel.de>
347 Viljo Marrandi C<vilts@yahoo.com>
348 Marcus Ramberg C<mramberg@cpan.org>
349
350 =head1 COPYRIGHT
351
352 This program is free software, you can redistribute it and/or modify it
353 under the same terms as Perl itself.