Fixed: assorted typos
[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->param->{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 $filename = $upload->filename;
131                 my $target   = "/tmp/upload/$filename";
132                 
133                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
134                     die( "Failed to copy '$filename' to '$target': $!" );
135                 }
136             }
137         }
138
139         $c->stash->{template} = 'file_upload.html';
140     }
141
142 C<for my $field ($c-E<gt>req->upload)> loops automatically over all file input
143 fields and gets input names. After that is basic file saving code, just like in
144 single file upload.
145
146 Notice: C<die>ing might not be what you want to do, when an error occurs, but
147 it works as an example. A better idea would be to store error C<$!> in
148 $c->stash->{error} and show a custom error template displaying this message.
149
150 For more information about uploads and usable methods look at
151 C<Catalyst::Request::Upload> and C<Catalyst::Request>.
152
153 =head2 Authentication with Catalyst::Plugin::Authentication::CDBI
154
155 There are (at least) two ways to implement authentication with this plugin:
156 1) only checking username and password;
157 2) checking username, password and the roles the user has
158
159 For both variants you'll need the following code in your MyApp package:
160
161     use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
162
163     MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
164                                        user_field => 'email',
165                                        password_field => 'password' });
166
167 'user_class' is a Class::DBI class for your users table.
168 'user_field' tells which field is used for username lookup (might be 
169 email, first name, surname etc.).
170 'password_field' is, well, password field in your table and by default 
171 password is stored in plain text. Authentication::CDBI looks for 'user' 
172 and 'password' fields in table, if they're not defined in the config.
173
174 In PostgreSQL, the users table might be something like:
175
176  CREATE TABLE users (
177    user_id   serial,
178    name      varchar(100),
179    surname   varchar(100),
180    password  varchar(100),
181    email     varchar(100),
182    primary key(user_id)
183  );
184
185 We'll discuss the first variant for now:
186 1. user:password login/auth without roles
187
188 To log in a user you might use an action like this:
189
190     sub 'login' : Local {
191         my ($self, $c) = @_;
192         if ($c->req->params->{username}) {
193             $c->session_login($c->req->params->{username}, 
194                               $c->req->params->{password} );
195             if ($c->req->{user}) {
196                 $c->forward('?restricted_area');
197             }
198         }
199     }
200
201 $c->req->params->{username} and $c->req->params->{password} are html 
202 form parameters from a login form. If login succeeds, then 
203 $c->req->{user} contains the username of the authenticated user.
204
205 If you want to remember the user's login status in between further 
206 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will 
207 create a session id and session cookie and automatically append session 
208 id to all urls. So all you have to do is just check $c->req->{user} 
209 where needed.
210
211 To log out a user, just call $c->session_logout.
212
213 Now let's take a look at the second variant:
214 2. user:password login/auth with roles
215
216 To use roles you need to add the following parameters to  MyApp->config in the 'authentication' section:
217
218     role_class      => 'MyApp::M::MyApp::Roles',
219     user_role_class => 'MyApp::M::MyApp::UserRoles',
220     user_role_user_field => 'user_id',
221     user_role_role_field => 'role_id',
222
223 Corresponding tables in PostgreSQL could look like this:
224
225  CREATE TABLE roles (
226    role_id  serial,
227    name     varchar(100),
228    primary key(role_id)
229  );
230
231  CREATE TABLE user_roles (
232    user_role_id  serial,
233    user_id       int,
234    role_id       int,
235    primary key(user_role_id),
236    foreign key(user_id) references users(user_id),
237    foreign key(role_id) references roles(role_id)
238  );
239
240 The 'roles' table is a list of role names and the 'user_role' table is 
241 used for the user -> role lookup.
242
243 Now if a logged-in user wants to see a location which is allowed only 
244 for people with an 'admin' role, in your controller you can check it 
245 with:
246
247     sub add : Local {
248         my ($self, $c) = @_;
249         if ($c->roles(qw/admin/)) {
250             $c->req->output("Your account has the role 'admin.'");
251         } else {
252             $c->req->output("You're not allowed to be here.");
253         }
254     }
255
256 One thing you might need is to forward non-authenticated users to a login 
257 form if they try to access restricted areas. If you want to do this 
258 controller-wide (if you have one controller for your admin section) then it's 
259 best to add a user check to a '!begin' action:
260
261     sub begin : Private {
262         my ($self, $c) = @_;
263         unless ($c->req->{user}) {
264             $c->req->action(undef);  ## notice this!!
265             $c->forward('?login');
266         }
267     }
268
269 Pay attention to $c->req->action(undef). This is needed because of the 
270 way $c->forward works - C<forward> to C<login> gets called, but after that 
271 Catalyst will still execute the action defined in the URI (e.g. if you 
272 tried to go to C</add>, then first 'begin' will forward to 'login', but after
273 that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any 
274 actions that were to be called and forwards the user where we want him/her 
275 to be.
276
277 And this is all you need to do. 
278
279
280 =head2 How to use Catalyst without mod_perl
281
282 Catalyst applications give optimum performance when run under mod_perl.
283 However sometimes mod_perl is not an option, and running under CGI is 
284 just too slow.  There's also an alternative to mod_perl that gives
285 reasonable performance named FastCGI.
286
287 B<Using FastCGI>
288
289 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
290 independent, scalable, extension to CGI that provides high performance 
291 without the limitations of specific server APIs."  Web server support 
292 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
293 support in the C<FCGI> module.  To convert a CGI Catalyst application 
294 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
295 while the C<Accept> method returns zero.  The following code shows how 
296 it is done - and it also works as a normal, single-shot CGI script.
297
298     #!/usr/bin/perl
299     use strict;
300     use FCGI;
301     use MyApp;
302
303     my $request = FCGI::Request();
304     while ($request->Accept() >= 0) {
305         MyApp->run;
306     }
307
308 Any initialization code should be included outside the request-accept 
309 loop.
310
311 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
312 complete HTTP response including the status line (e.g.: 
313 "C<HTTP/1.1 200>").
314 FastCGI just wants a set of headers, so the sample code captures the 
315 output and  drops the first line if it is an HTTP status line (note: 
316 this may change).
317
318 The Apache C<mod_fastcgi> module is provided by a number of Linux 
319 distros and is straightforward to compile for most Unix-like systems.  
320 The module provides a FastCGI Process Manager, which manages FastCGI 
321 scripts.  You configure your script as a FastCGI script with the 
322 following Apache configuration directives:
323
324     <Location /fcgi-bin>
325        AddHandler fastcgi-script fcgi
326     </Location>
327
328 or:
329
330     <Location /fcgi-bin>
331        SetHandler fastcgi-script
332        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
333     </Location>
334
335 C<mod_fastcgi> provides a number of options for controlling the FastCGI
336 scripts spawned; it also allows scripts to be run to handle the
337 authentication, authorization, and access check phases.
338
339 For more information see the FastCGI documentation, the C<FCGI> module 
340 and L<http://www.fastcgi.com/>.
341
342 =head1 AUTHOR
343
344 Sebastian Riedel, C<sri@oook.de>
345 Danijel Milicevic C<me@danijel.de>
346 Viljo Marrandi C<vilts@yahoo.com>
347 Marcus Ramberg C<mramberg@cpan.org>
348
349 =head1 COPYRIGHT
350
351 This program is free software, you can redistribute it and/or modify it
352 under the same terms as Perl itself.