fixed borked Cookbook merge
[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
eff5f524 13You can force Catalyst to display the debug screen at the end of the request by
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
379ca371 21If you're tired of removing and adding this all the time, you can add a
22condition in the C<end> action. For example:
23
24 sub end : Private {
25 my ( $self, $c ) = @_;
26 die "forced debug" if $c->req->params->{dump_info};
27 }
28
29Then just add to your query string C<"&dump_info=1">, or the like, to
30force debug output.
aff93052 31
aff93052 32
fc7ec1d9 33=head2 Disable statistics
34
35Just add this line to your application class if you don't want those nifty
36statistics in your debug messages.
37
38 sub Catalyst::Log::info { }
39
40=head2 Scaffolding
41
42Scaffolding is very simple with Catalyst.
51ef2818 43Just use Catalyst::Model::CDBI::CRUD as your base class.
fc7ec1d9 44
45 # lib/MyApp/Model/CDBI.pm
46 package MyApp::Model::CDBI;
47
48 use strict;
49 use base 'Catalyst::Model::CDBI::CRUD';
50
51 __PACKAGE__->config(
52 dsn => 'dbi:SQLite:/tmp/myapp.db',
53 relationships => 1
54 );
55
56 1;
57
58 # lib/MyApp.pm
59 package MyApp;
60
61 use Catalyst 'FormValidator';
62
63 __PACKAGE__->config(
64 name => 'My Application',
65 root => '/home/joeuser/myapp/root'
66 );
67
61b1e958 68 sub my_table : Global {
69 my ( $self, $c ) = @_;
70 $c->form( optional => [ MyApp::Model::CDBI::Table->columns ] );
71 $c->forward('MyApp::Model::CDBI::Table');
72 }
fc7ec1d9 73
74 1;
75
379ca371 76Modify the C<$c-E<gt>form()> parameters to match your needs, and don't
77forget to copy the templates into the template root. Can't find the
78templates? They were in the CRUD model distribution, so you can do
79B<look Catalyst::Model::CDBI::CRUD> from the CPAN shell to find them.
80
81Other Scaffolding modules are in development at the time of writing.
fc7ec1d9 82
5c0ff128 83=head2 Single file upload with Catalyst
aba94964 84
85To implement uploads in Catalyst you need to have a HTML form similiar to
86this:
87
88 <form action="/upload" method="post" enctype="multipart/form-data">
89 <input type="hidden" name="form_submit" value="yes">
90 <input type="file" name="my_file">
91 <input type="submit" value="Send">
92 </form>
93
379ca371 94It's very important not to forget C<enctype="multipart/form-data"> in
95the form.
aba94964 96
97Catalyst Controller module 'upload' action:
98
5c0ff128 99 sub upload : Global {
100 my ($self, $c) = @_;
4d89569d 101
102 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
103
104 if ( my $upload = $c->request->upload('my_file') ) {
47ae6960 105
5c0ff128 106 my $filename = $upload->filename;
47ae6960 107 my $target = "/tmp/upload/$filename";
108
3ffaf022 109 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 110 die( "Failed to copy '$filename' to '$target': $!" );
5c0ff128 111 }
5c0ff128 112 }
113 }
4d89569d 114
5c0ff128 115 $c->stash->{template} = 'file_upload.html';
116 }
117
118=head2 Multiple file upload with Catalyst
119
379ca371 120Code for uploading multiple files from one form needs a few changes:
5c0ff128 121
379ca371 122The form should have this basic structure:
5c0ff128 123
124 <form action="/upload" method="post" enctype="multipart/form-data">
125 <input type="hidden" name="form_submit" value="yes">
126 <input type="file" name="file1" size="50"><br>
127 <input type="file" name="file2" size="50"><br>
128 <input type="file" name="file3" size="50"><br>
129 <input type="submit" value="Send">
130 </form>
131
379ca371 132And in the controller:
5c0ff128 133
134 sub upload : Local {
135 my ($self, $c) = @_;
4d89569d 136
137 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
138
139 for my $field ( $c->req->upload ) {
140
02a53b81 141 my $upload = $c->req->upload($field);
4d89569d 142 my $filename = $upload->filename;
47ae6960 143 my $target = "/tmp/upload/$filename";
144
3ffaf022 145 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 146 die( "Failed to copy '$filename' to '$target': $!" );
aba94964 147 }
148 }
61b1e958 149 }
4d89569d 150
5c0ff128 151 $c->stash->{template} = 'file_upload.html';
152 }
153
379ca371 154C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
155input fields and gets input names. After that is basic file saving code,
156just like in single file upload.
aba94964 157
379ca371 158Notice: C<die>ing might not be what you want to do, when an error
159occurs, but it works as an example. A better idea would be to store
160error C<$!> in $c->stash->{error} and show a custom error template
161displaying this message.
aba94964 162
5c0ff128 163For more information about uploads and usable methods look at
379ca371 164L<Catalyst::Request::Upload> and L<Catalyst::Request>.
aba94964 165
deb90705 166=head2 Authentication with Catalyst::Plugin::Authentication::CDBI
167
168There are (at least) two ways to implement authentication with this plugin:
eff5f524 1691) only checking username and password;
379ca371 1702) checking username, password, and the roles the user has
deb90705 171
172For both variants you'll need the following code in your MyApp package:
173
174 use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
175
176 MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
177 user_field => 'email',
178 password_field => 'password' });
179
180'user_class' is a Class::DBI class for your users table.
181'user_field' tells which field is used for username lookup (might be
51ef2818 182email, first name, surname etc.).
deb90705 183'password_field' is, well, password field in your table and by default
184password is stored in plain text. Authentication::CDBI looks for 'user'
185and 'password' fields in table, if they're not defined in the config.
186
51ef2818 187In PostgreSQL, the users table might be something like:
deb90705 188
51ef2818 189 CREATE TABLE users (
190 user_id serial,
191 name varchar(100),
192 surname varchar(100),
193 password varchar(100),
194 email varchar(100),
195 primary key(user_id)
196 );
deb90705 197
198We'll discuss the first variant for now:
51ef2818 1991. user:password login/auth without roles
deb90705 200
51ef2818 201To log in a user you might use an action like this:
deb90705 202
7c1078a4 203 sub login : Local {
deb90705 204 my ($self, $c) = @_;
205 if ($c->req->params->{username}) {
206 $c->session_login($c->req->params->{username},
61b1e958 207 $c->req->params->{password} );
deb90705 208 if ($c->req->{user}) {
209 $c->forward('?restricted_area');
210 }
211 }
61b1e958 212 }
deb90705 213
7c1078a4 214This action should not go in your MyApp class...if it does, it will
215conflict with the built-in method of the same name. Instead, put it
216in a Controller class.
217
deb90705 218$c->req->params->{username} and $c->req->params->{password} are html
61b1e958 219form parameters from a login form. If login succeeds, then
220$c->req->{user} contains the username of the authenticated user.
deb90705 221
51ef2818 222If you want to remember the user's login status in between further
223requests, then just use the C<$c-E<gt>session_login> method. Catalyst will
224create a session id and session cookie and automatically append session
225id to all urls. So all you have to do is just check $c->req->{user}
61b1e958 226where needed.
deb90705 227
51ef2818 228To log out a user, just call $c->session_logout.
deb90705 229
51ef2818 230Now let's take a look at the second variant:
2312. user:password login/auth with roles
deb90705 232
51ef2818 233To use roles you need to add the following parameters to MyApp->config in the 'authentication' section:
deb90705 234
235 role_class => 'MyApp::M::MyApp::Roles',
236 user_role_class => 'MyApp::M::MyApp::UserRoles',
237 user_role_user_field => 'user_id',
238 user_role_role_field => 'role_id',
239
240Corresponding tables in PostgreSQL could look like this:
241
51ef2818 242 CREATE TABLE roles (
243 role_id serial,
244 name varchar(100),
245 primary key(role_id)
246 );
247
248 CREATE TABLE user_roles (
249 user_role_id serial,
250 user_id int,
251 role_id int,
252 primary key(user_role_id),
253 foreign key(user_id) references users(user_id),
254 foreign key(role_id) references roles(role_id)
255 );
deb90705 256
61b1e958 257The 'roles' table is a list of role names and the 'user_role' table is
258used for the user -> role lookup.
deb90705 259
51ef2818 260Now if a logged-in user wants to see a location which is allowed only
261for people with an 'admin' role, in your controller you can check it
61b1e958 262with:
deb90705 263
61b1e958 264 sub add : Local {
deb90705 265 my ($self, $c) = @_;
266 if ($c->roles(qw/admin/)) {
267 $c->req->output("Your account has the role 'admin.'");
268 } else {
51ef2818 269 $c->req->output("You're not allowed to be here.");
deb90705 270 }
61b1e958 271 }
deb90705 272
51ef2818 273One thing you might need is to forward non-authenticated users to a login
274form if they try to access restricted areas. If you want to do this
275controller-wide (if you have one controller for your admin section) then it's
276best to add a user check to a '!begin' action:
deb90705 277
61b1e958 278 sub begin : Private {
deb90705 279 my ($self, $c) = @_;
280 unless ($c->req->{user}) {
281 $c->req->action(undef); ## notice this!!
282 $c->forward('?login');
283 }
61b1e958 284 }
deb90705 285
51ef2818 286Pay attention to $c->req->action(undef). This is needed because of the
287way $c->forward works - C<forward> to C<login> gets called, but after that
288Catalyst will still execute the action defined in the URI (e.g. if you
289tried to go to C</add>, then first 'begin' will forward to 'login', but after
290that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any
291actions that were to be called and forwards the user where we want him/her
deb90705 292to be.
293
51ef2818 294And this is all you need to do.
deb90705 295
afb208ae 296=head2 Pass-through login (and other actions)
297
eff5f524 298An easy way of having assorted actions that occur during the processing
299of a request that are orthogonal to its actual purpose - logins, silent
afb208ae 300commands etc. Provide actions for these, but when they're required for
eff5f524 301something else fill e.g. a form variable __login and have a sub begin
302like so:
afb208ae 303
eff5f524 304 sub begin : Private {
305 my ($self, $c) = @_;
306 foreach my $action (qw/login docommand foo bar whatever/) {
307 if ($c->req->params->{"__${action}"}) {
308 $c->forward($action);
309 }
310 }
afb208ae 311 }
145074c2 312
313=head2 How to use Catalyst without mod_perl
314
315Catalyst applications give optimum performance when run under mod_perl.
61b1e958 316However sometimes mod_perl is not an option, and running under CGI is
51ef2818 317just too slow. There's also an alternative to mod_perl that gives
dec2a2a9 318reasonable performance named FastCGI.
145074c2 319
320B<Using FastCGI>
321
61b1e958 322To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
323independent, scalable, extension to CGI that provides high performance
324without the limitations of specific server APIs." Web server support
325is provided for Apache in the form of C<mod_fastcgi> and there is Perl
326support in the C<FCGI> module. To convert a CGI Catalyst application
327to FastCGI one needs to initialize an C<FCGI::Request> object and loop
328while the C<Accept> method returns zero. The following code shows how
329it is done - and it also works as a normal, single-shot CGI script.
145074c2 330
331 #!/usr/bin/perl
332 use strict;
333 use FCGI;
334 use MyApp;
335
336 my $request = FCGI::Request();
337 while ($request->Accept() >= 0) {
1c61c726 338 MyApp->run;
145074c2 339 }
340
61b1e958 341Any initialization code should be included outside the request-accept
342loop.
145074c2 343
51ef2818 344There is one little complication, which is that C<MyApp-E<gt>run> outputs a
61b1e958 345complete HTTP response including the status line (e.g.:
346"C<HTTP/1.1 200>").
347FastCGI just wants a set of headers, so the sample code captures the
348output and drops the first line if it is an HTTP status line (note:
349this may change).
350
351The Apache C<mod_fastcgi> module is provided by a number of Linux
352distros and is straightforward to compile for most Unix-like systems.
353The module provides a FastCGI Process Manager, which manages FastCGI
354scripts. You configure your script as a FastCGI script with the
355following Apache configuration directives:
145074c2 356
357 <Location /fcgi-bin>
358 AddHandler fastcgi-script fcgi
359 </Location>
360
361or:
362
363 <Location /fcgi-bin>
364 SetHandler fastcgi-script
365 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
366 </Location>
367
368C<mod_fastcgi> provides a number of options for controlling the FastCGI
369scripts spawned; it also allows scripts to be run to handle the
51ef2818 370authentication, authorization, and access check phases.
145074c2 371
61b1e958 372For more information see the FastCGI documentation, the C<FCGI> module
373and L<http://www.fastcgi.com/>.
eff5f524 374
379ca371 375=head2 Serving static content
376
377Serving static content in Catalyst can be somewhat tricky; this recipe
378shows one possible solution. Using this recipe will serve all static
379content through Catalyst when developing with the built-in HTTP::Daemon
380server, and will make it easy to use Apache to serve the content when
381your app goes into production.
382
383Static content is best served from a single directory within your root
384directory. Having many different directories such as C<root/css> and
385C<root/images> requires more code to manage, because you must separately
386identify each static directory--if you decide to add a C<root/js>
387directory, you'll need to change your code to account for it. In
388contrast, keeping all static directories as subdirectories of a main
389C<root/static> directory makes things much easier to manager. Here's an
390example of a typical root directory structure:
391
392 root/
393 root/content.tt
394 root/controller/stuff.tt
395 root/header.tt
396 root/static/
397 root/static/css/main.css
398 root/static/images/logo.jpg
399 root/static/js/code.js
400
401
402All static content lives under C<root/static> with everything else being
403Template Toolkit files. Now you can identify the static content by
404matching C<static> from within Catalyst.
405
406=head3 Serving with HTTP::Daemon (myapp_server.pl)
407
408To serve these files under the standalone server, we first must load the
409Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
410installed.
411
412In your main application class (MyApp.pm), load the plugin:
413
414 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
415
416You will also need to make sure your end method does I<not> forward
417static content to the view, perhaps like this:
418
419 sub end : Private {
420 my ( $self, $c ) = @_;
421
422 $c->forward( 'MyApp::V::TT' )
423 unless ( $c->res->body || !$c->stash->{template} );
424 }
425
426This code will only forward to the view if a template has been
427previously defined by a controller and if there is not already data in
428C<$c-E<gt>res-E<gt>body>.
429
430Next, create a controller to handle requests for the /static path. Use
431the Helper to save time. This command will create a stub controller as
432C<lib/MyApp/C/Static.pm>.
433
434 $ script/myapp_create.pl controller Static
435
436Edit the file and add the following methods:
437
438 # serve all files under /static as static files
439 sub default : Path('/static') {
440 my ( $self, $c ) = @_;
441
442 # Optional, allow the browser to cache the content
443 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
444
445 $c->serve_static; # from Catalyst::Plugin::Static
446 }
447
448 # also handle requests for /favicon.ico
449 sub favicon : Path('/favicon.ico') {
450 my ( $self, $c ) = @_;
451
452 $c->serve_static;
453 }
454
455You can also define a different icon for the browser to use instead of
456favicon.ico by using this in your HTML header:
457
458 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
459
460=head3 Common problems
461
462The Static plugin makes use of the C<shared-mime-info> package to
463automatically determine MIME types. This package is notoriously
464difficult to install, especially on win32 and OSX. For OSX the easiest
465path might be to install Fink, then use C<apt-get install
466shared-mime-info>. Restart the server, and everything should be fine.
467
468Make sure you are using the latest version (>= 0.16) for best
469results. If you are having errors serving CSS files, or if they get
470served as text/plain instead of text/css, you may have an outdated
471shared-mime-info version. You may also wish to simply use the following
472code in your Static controller:
473
474 if ($c->req->path =~ /css$/i) {
475 $c->serve_static( "text/css" );
476 } else {
477 $c->serve_static;
478 }
479
480=head3 Serving with Apache
481
482When using Apache, you can completely bypass Catalyst and the Static
483controller by intercepting requests for the C<root/static> path at the
484server level. All that is required is to define a DocumentRoot and add a
485separate Location block for your static content. Here is a complete
486config for this application under mod_perl 1.x; variations, some of
487which could be simpler, are left as an exercise for the reader:
488
489 <Perl>
490 use lib qw(/var/www/MyApp/lib);
491 </Perl>
492 PerlModule MyApp
493
494 <VirtualHost *>
495 ServerName myapp.example.com
496 DocumentRoot /var/www/MyApp/root
497 <Location />
498 SetHandler perl-script
499 PerlHandler MyApp
500 </Location>
501 <LocationMatch "/(static|favicon.ico)">
502 SetHandler default-handler
503 </LocationMatch>
504 </VirtualHost>
505
506=head2 Forwarding with arguments
145074c2 507
eff5f524 508Sometimes you want to pass along arguments when forwarding to another
509action. As of version 5.30, arguments can be passed in the call to
510C<forward>; in earlier versions, you can manually set the arguments in
511the Catalyst Request object:
e6394847 512
eff5f524 513 # version 5.30 and later:
514 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
2343e117 515
eff5f524 516 # pre-5.30
2343e117 517 $c->req->args([qw/arg1 arg2 arg3/]);
518 $c->forward('/wherever');
519
eff5f524 520(See L<Catalyst::Manual::Intro#Flow_Control> for more information on
521passing arguments via C<forward>.)
522
fc7ec1d9 523=head1 AUTHOR
524
525Sebastian Riedel, C<sri@oook.de>
379ca371 526Danijel Milicevic, C<me@danijel.de>
527Viljo Marrandi, C<vilts@yahoo.com>
528Marcus Ramberg, C<mramberg@cpan.org>
529Andy Grundman, C<andy@hybridized.org>
eff5f524 530Marcus Ramberg C<mramberg@cpan.org>
fc7ec1d9 531
532=head1 COPYRIGHT
533
61b1e958 534This program is free software, you can redistribute it and/or modify it
535under the same terms as Perl itself.