Added attributes for detach
[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
13You can force Catalyst to display the debug screen at the end of the request by
51ef2818 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
51ef2818 22can easily add a condition. For example:
aff93052 23
2343e117 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
69Modify the $c->form() parameters to match your needs, and don't forget to copy
b114f908 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
51ef2818 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
110Code for uploading multiple files from one form needs little changes compared
111to single file upload.
112
113Form 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
123Controller:
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
51ef2818 145C<for my $field ($c-E<gt>req->upload)> loops automatically over all file input
5c0ff128 146fields and gets input names. After that is basic file saving code, just like in
147single file upload.
aba94964 148
51ef2818 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
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:
51ef2818 1591) only checking username and password;
deb90705 1602) checking username, password and the roles the user has
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
288An easy way of having assorted actions that occur during the processing of
289a request that are orthogonal to its actual purpose - logins, silent
290commands etc. Provide actions for these, but when they're required for
291something else fill e.g. a form variable __login and have a sub begin like so:
292
293sub 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}
145074c2 301
302=head2 How to use Catalyst without mod_perl
303
304Catalyst applications give optimum performance when run under mod_perl.
61b1e958 305However sometimes mod_perl is not an option, and running under CGI is
51ef2818 306just too slow. There's also an alternative to mod_perl that gives
dec2a2a9 307reasonable performance named FastCGI.
145074c2 308
309B<Using FastCGI>
310
61b1e958 311To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
312independent, scalable, extension to CGI that provides high performance
313without the limitations of specific server APIs." Web server support
314is provided for Apache in the form of C<mod_fastcgi> and there is Perl
315support in the C<FCGI> module. To convert a CGI Catalyst application
316to FastCGI one needs to initialize an C<FCGI::Request> object and loop
317while the C<Accept> method returns zero. The following code shows how
318it is done - and it also works as a normal, single-shot CGI script.
145074c2 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) {
1c61c726 327 MyApp->run;
145074c2 328 }
329
61b1e958 330Any initialization code should be included outside the request-accept
331loop.
145074c2 332
51ef2818 333There is one little complication, which is that C<MyApp-E<gt>run> outputs a
61b1e958 334complete HTTP response including the status line (e.g.:
335"C<HTTP/1.1 200>").
336FastCGI just wants a set of headers, so the sample code captures the
337output and drops the first line if it is an HTTP status line (note:
338this may change).
339
340The Apache C<mod_fastcgi> module is provided by a number of Linux
341distros and is straightforward to compile for most Unix-like systems.
342The module provides a FastCGI Process Manager, which manages FastCGI
343scripts. You configure your script as a FastCGI script with the
344following Apache configuration directives:
145074c2 345
346 <Location /fcgi-bin>
347 AddHandler fastcgi-script fcgi
348 </Location>
349
350or:
351
352 <Location /fcgi-bin>
353 SetHandler fastcgi-script
354 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
355 </Location>
356
357C<mod_fastcgi> provides a number of options for controlling the FastCGI
358scripts spawned; it also allows scripts to be run to handle the
51ef2818 359authentication, authorization, and access check phases.
145074c2 360
61b1e958 361For more information see the FastCGI documentation, the C<FCGI> module
362and L<http://www.fastcgi.com/>.
145074c2 363
2343e117 364=head1 Forwarding with a parameter
365
b284d6a7 366Sometimes you want to pass along arguments when forwarding to another
2343e117 367action. This can easily be accomplished like this:
368
369 $c->req->args([qw/arg1 arg2 arg3/]);
370 $c->forward('/wherever');
371
fc7ec1d9 372=head1 AUTHOR
373
374Sebastian Riedel, C<sri@oook.de>
deb90705 375Danijel Milicevic C<me@danijel.de>
376Viljo Marrandi C<vilts@yahoo.com>
61b1e958 377Marcus Ramberg C<mramberg@cpan.org>
fc7ec1d9 378
379=head1 COPYRIGHT
380
61b1e958 381This program is free software, you can redistribute it and/or modify it
382under the same terms as Perl itself.