Updated welcome
[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
822fe954 83=head2 File uploads
84
85=head3 Single file upload with Catalyst
aba94964 86
26e73131 87To implement uploads in Catalyst, you need to have a HTML form similar to
aba94964 88this:
89
90 <form action="/upload" method="post" enctype="multipart/form-data">
91 <input type="hidden" name="form_submit" value="yes">
92 <input type="file" name="my_file">
93 <input type="submit" value="Send">
94 </form>
95
379ca371 96It's very important not to forget C<enctype="multipart/form-data"> in
97the form.
aba94964 98
99Catalyst Controller module 'upload' action:
100
5c0ff128 101 sub upload : Global {
102 my ($self, $c) = @_;
4d89569d 103
104 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
105
106 if ( my $upload = $c->request->upload('my_file') ) {
47ae6960 107
5c0ff128 108 my $filename = $upload->filename;
47ae6960 109 my $target = "/tmp/upload/$filename";
110
3ffaf022 111 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 112 die( "Failed to copy '$filename' to '$target': $!" );
5c0ff128 113 }
5c0ff128 114 }
115 }
4d89569d 116
5c0ff128 117 $c->stash->{template} = 'file_upload.html';
118 }
119
822fe954 120=head3 Multiple file upload with Catalyst
5c0ff128 121
379ca371 122Code for uploading multiple files from one form needs a few changes:
5c0ff128 123
379ca371 124The form should have this basic structure:
5c0ff128 125
126 <form action="/upload" method="post" enctype="multipart/form-data">
127 <input type="hidden" name="form_submit" value="yes">
128 <input type="file" name="file1" size="50"><br>
129 <input type="file" name="file2" size="50"><br>
130 <input type="file" name="file3" size="50"><br>
131 <input type="submit" value="Send">
132 </form>
133
379ca371 134And in the controller:
5c0ff128 135
136 sub upload : Local {
137 my ($self, $c) = @_;
4d89569d 138
139 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
140
141 for my $field ( $c->req->upload ) {
142
02a53b81 143 my $upload = $c->req->upload($field);
4d89569d 144 my $filename = $upload->filename;
47ae6960 145 my $target = "/tmp/upload/$filename";
146
3ffaf022 147 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 148 die( "Failed to copy '$filename' to '$target': $!" );
aba94964 149 }
150 }
61b1e958 151 }
4d89569d 152
5c0ff128 153 $c->stash->{template} = 'file_upload.html';
154 }
155
379ca371 156C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
157input fields and gets input names. After that is basic file saving code,
158just like in single file upload.
aba94964 159
379ca371 160Notice: C<die>ing might not be what you want to do, when an error
161occurs, but it works as an example. A better idea would be to store
162error C<$!> in $c->stash->{error} and show a custom error template
163displaying this message.
aba94964 164
5c0ff128 165For more information about uploads and usable methods look at
379ca371 166L<Catalyst::Request::Upload> and L<Catalyst::Request>.
aba94964 167
deb90705 168=head2 Authentication with Catalyst::Plugin::Authentication::CDBI
169
170There are (at least) two ways to implement authentication with this plugin:
eff5f524 1711) only checking username and password;
379ca371 1722) checking username, password, and the roles the user has
deb90705 173
174For both variants you'll need the following code in your MyApp package:
175
176 use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
177
178 MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
179 user_field => 'email',
180 password_field => 'password' });
181
182'user_class' is a Class::DBI class for your users table.
183'user_field' tells which field is used for username lookup (might be
51ef2818 184email, first name, surname etc.).
deb90705 185'password_field' is, well, password field in your table and by default
186password is stored in plain text. Authentication::CDBI looks for 'user'
187and 'password' fields in table, if they're not defined in the config.
188
51ef2818 189In PostgreSQL, the users table might be something like:
deb90705 190
51ef2818 191 CREATE TABLE users (
192 user_id serial,
193 name varchar(100),
194 surname varchar(100),
195 password varchar(100),
196 email varchar(100),
197 primary key(user_id)
198 );
deb90705 199
200We'll discuss the first variant for now:
51ef2818 2011. user:password login/auth without roles
deb90705 202
51ef2818 203To log in a user you might use an action like this:
deb90705 204
7c1078a4 205 sub login : Local {
deb90705 206 my ($self, $c) = @_;
207 if ($c->req->params->{username}) {
208 $c->session_login($c->req->params->{username},
61b1e958 209 $c->req->params->{password} );
deb90705 210 if ($c->req->{user}) {
9bee7d37 211 $c->forward('/restricted_area');
deb90705 212 }
213 }
61b1e958 214 }
deb90705 215
7c1078a4 216This action should not go in your MyApp class...if it does, it will
217conflict with the built-in method of the same name. Instead, put it
218in a Controller class.
219
deb90705 220$c->req->params->{username} and $c->req->params->{password} are html
61b1e958 221form parameters from a login form. If login succeeds, then
222$c->req->{user} contains the username of the authenticated user.
deb90705 223
51ef2818 224If you want to remember the user's login status in between further
225requests, then just use the C<$c-E<gt>session_login> method. Catalyst will
226create a session id and session cookie and automatically append session
227id to all urls. So all you have to do is just check $c->req->{user}
61b1e958 228where needed.
deb90705 229
51ef2818 230To log out a user, just call $c->session_logout.
deb90705 231
51ef2818 232Now let's take a look at the second variant:
2332. user:password login/auth with roles
deb90705 234
51ef2818 235To use roles you need to add the following parameters to MyApp->config in the 'authentication' section:
deb90705 236
237 role_class => 'MyApp::M::MyApp::Roles',
238 user_role_class => 'MyApp::M::MyApp::UserRoles',
239 user_role_user_field => 'user_id',
240 user_role_role_field => 'role_id',
241
242Corresponding tables in PostgreSQL could look like this:
243
51ef2818 244 CREATE TABLE roles (
245 role_id serial,
246 name varchar(100),
247 primary key(role_id)
248 );
249
250 CREATE TABLE user_roles (
251 user_role_id serial,
252 user_id int,
253 role_id int,
254 primary key(user_role_id),
255 foreign key(user_id) references users(user_id),
256 foreign key(role_id) references roles(role_id)
257 );
deb90705 258
61b1e958 259The 'roles' table is a list of role names and the 'user_role' table is
260used for the user -> role lookup.
deb90705 261
51ef2818 262Now if a logged-in user wants to see a location which is allowed only
263for people with an 'admin' role, in your controller you can check it
61b1e958 264with:
deb90705 265
61b1e958 266 sub add : Local {
deb90705 267 my ($self, $c) = @_;
268 if ($c->roles(qw/admin/)) {
016373e6 269 $c->res->output("Your account has the role 'admin.'");
deb90705 270 } else {
016373e6 271 $c->res->output("You're not allowed to be here.");
deb90705 272 }
61b1e958 273 }
deb90705 274
51ef2818 275One thing you might need is to forward non-authenticated users to a login
276form if they try to access restricted areas. If you want to do this
277controller-wide (if you have one controller for your admin section) then it's
9bee7d37 278best to add a user check to a 'begin' action:
deb90705 279
61b1e958 280 sub begin : Private {
deb90705 281 my ($self, $c) = @_;
282 unless ($c->req->{user}) {
283 $c->req->action(undef); ## notice this!!
9bee7d37 284 $c->forward('/user/login');
deb90705 285 }
61b1e958 286 }
deb90705 287
26e73131 288Pay attention to $c->req->action(undef). This is needed because of the
289way $c->forward works - C<forward> to C<login> gets called, but after
290that Catalyst will still execute the action defined in the URI (e.g. if
291you tried to go to C</add>, then first 'begin' will forward to 'login',
292but after that 'add' will nonetheless be executed). So
293$c->req->action(undef) undefines any actions that were to be called and
294forwards the user where we want him/her to be.
deb90705 295
51ef2818 296And this is all you need to do.
deb90705 297
afb208ae 298=head2 Pass-through login (and other actions)
299
eff5f524 300An easy way of having assorted actions that occur during the processing
301of a request that are orthogonal to its actual purpose - logins, silent
afb208ae 302commands etc. Provide actions for these, but when they're required for
eff5f524 303something else fill e.g. a form variable __login and have a sub begin
304like so:
afb208ae 305
eff5f524 306 sub begin : Private {
307 my ($self, $c) = @_;
308 foreach my $action (qw/login docommand foo bar whatever/) {
309 if ($c->req->params->{"__${action}"}) {
310 $c->forward($action);
311 }
312 }
afb208ae 313 }
145074c2 314
315=head2 How to use Catalyst without mod_perl
316
317Catalyst applications give optimum performance when run under mod_perl.
61b1e958 318However sometimes mod_perl is not an option, and running under CGI is
51ef2818 319just too slow. There's also an alternative to mod_perl that gives
dec2a2a9 320reasonable performance named FastCGI.
145074c2 321
822fe954 322=head3 Using FastCGI
145074c2 323
61b1e958 324To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
325independent, scalable, extension to CGI that provides high performance
326without the limitations of specific server APIs." Web server support
327is provided for Apache in the form of C<mod_fastcgi> and there is Perl
328support in the C<FCGI> module. To convert a CGI Catalyst application
329to FastCGI one needs to initialize an C<FCGI::Request> object and loop
330while the C<Accept> method returns zero. The following code shows how
331it is done - and it also works as a normal, single-shot CGI script.
145074c2 332
333 #!/usr/bin/perl
334 use strict;
335 use FCGI;
336 use MyApp;
337
338 my $request = FCGI::Request();
339 while ($request->Accept() >= 0) {
1c61c726 340 MyApp->run;
145074c2 341 }
342
61b1e958 343Any initialization code should be included outside the request-accept
344loop.
145074c2 345
51ef2818 346There is one little complication, which is that C<MyApp-E<gt>run> outputs a
61b1e958 347complete HTTP response including the status line (e.g.:
348"C<HTTP/1.1 200>").
349FastCGI just wants a set of headers, so the sample code captures the
350output and drops the first line if it is an HTTP status line (note:
351this may change).
352
353The Apache C<mod_fastcgi> module is provided by a number of Linux
354distros and is straightforward to compile for most Unix-like systems.
355The module provides a FastCGI Process Manager, which manages FastCGI
356scripts. You configure your script as a FastCGI script with the
357following Apache configuration directives:
145074c2 358
359 <Location /fcgi-bin>
360 AddHandler fastcgi-script fcgi
361 </Location>
362
363or:
364
365 <Location /fcgi-bin>
366 SetHandler fastcgi-script
367 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
368 </Location>
369
370C<mod_fastcgi> provides a number of options for controlling the FastCGI
371scripts spawned; it also allows scripts to be run to handle the
51ef2818 372authentication, authorization, and access check phases.
145074c2 373
61b1e958 374For more information see the FastCGI documentation, the C<FCGI> module
375and L<http://www.fastcgi.com/>.
eff5f524 376
379ca371 377=head2 Serving static content
378
379Serving static content in Catalyst can be somewhat tricky; this recipe
380shows one possible solution. Using this recipe will serve all static
381content through Catalyst when developing with the built-in HTTP::Daemon
382server, and will make it easy to use Apache to serve the content when
383your app goes into production.
384
385Static content is best served from a single directory within your root
386directory. Having many different directories such as C<root/css> and
387C<root/images> requires more code to manage, because you must separately
388identify each static directory--if you decide to add a C<root/js>
389directory, you'll need to change your code to account for it. In
390contrast, keeping all static directories as subdirectories of a main
391C<root/static> directory makes things much easier to manager. Here's an
392example of a typical root directory structure:
393
394 root/
395 root/content.tt
396 root/controller/stuff.tt
397 root/header.tt
398 root/static/
399 root/static/css/main.css
400 root/static/images/logo.jpg
401 root/static/js/code.js
402
403
404All static content lives under C<root/static> with everything else being
405Template Toolkit files. Now you can identify the static content by
406matching C<static> from within Catalyst.
407
408=head3 Serving with HTTP::Daemon (myapp_server.pl)
409
410To serve these files under the standalone server, we first must load the
411Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
412installed.
413
414In your main application class (MyApp.pm), load the plugin:
415
416 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
417
418You will also need to make sure your end method does I<not> forward
419static content to the view, perhaps like this:
420
421 sub end : Private {
422 my ( $self, $c ) = @_;
423
424 $c->forward( 'MyApp::V::TT' )
425 unless ( $c->res->body || !$c->stash->{template} );
426 }
427
428This code will only forward to the view if a template has been
429previously defined by a controller and if there is not already data in
430C<$c-E<gt>res-E<gt>body>.
431
432Next, create a controller to handle requests for the /static path. Use
433the Helper to save time. This command will create a stub controller as
434C<lib/MyApp/C/Static.pm>.
435
436 $ script/myapp_create.pl controller Static
437
438Edit the file and add the following methods:
439
440 # serve all files under /static as static files
441 sub default : Path('/static') {
442 my ( $self, $c ) = @_;
443
444 # Optional, allow the browser to cache the content
445 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
446
447 $c->serve_static; # from Catalyst::Plugin::Static
448 }
449
450 # also handle requests for /favicon.ico
451 sub favicon : Path('/favicon.ico') {
452 my ( $self, $c ) = @_;
453
454 $c->serve_static;
455 }
456
457You can also define a different icon for the browser to use instead of
458favicon.ico by using this in your HTML header:
459
460 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
461
462=head3 Common problems
463
464The Static plugin makes use of the C<shared-mime-info> package to
465automatically determine MIME types. This package is notoriously
26e73131 466difficult to install, especially on win32 and OS X. For OS X the easiest
379ca371 467path might be to install Fink, then use C<apt-get install
468shared-mime-info>. Restart the server, and everything should be fine.
469
470Make sure you are using the latest version (>= 0.16) for best
471results. If you are having errors serving CSS files, or if they get
472served as text/plain instead of text/css, you may have an outdated
473shared-mime-info version. You may also wish to simply use the following
474code in your Static controller:
475
476 if ($c->req->path =~ /css$/i) {
477 $c->serve_static( "text/css" );
478 } else {
479 $c->serve_static;
480 }
481
482=head3 Serving with Apache
483
484When using Apache, you can completely bypass Catalyst and the Static
485controller by intercepting requests for the C<root/static> path at the
486server level. All that is required is to define a DocumentRoot and add a
487separate Location block for your static content. Here is a complete
6d23e1f4 488config for this application under mod_perl 1.x:
379ca371 489
490 <Perl>
491 use lib qw(/var/www/MyApp/lib);
492 </Perl>
493 PerlModule MyApp
494
495 <VirtualHost *>
496 ServerName myapp.example.com
497 DocumentRoot /var/www/MyApp/root
498 <Location />
499 SetHandler perl-script
500 PerlHandler MyApp
501 </Location>
502 <LocationMatch "/(static|favicon.ico)">
503 SetHandler default-handler
504 </LocationMatch>
505 </VirtualHost>
506
6d23e1f4 507And here's a simpler example that'll get you started:
508
509 Alias /static/ "/my/static/files/"
510 <Location "/static">
511 SetHandler none
512 </Location>
513
379ca371 514=head2 Forwarding with arguments
145074c2 515
eff5f524 516Sometimes you want to pass along arguments when forwarding to another
517action. As of version 5.30, arguments can be passed in the call to
518C<forward>; in earlier versions, you can manually set the arguments in
519the Catalyst Request object:
e6394847 520
eff5f524 521 # version 5.30 and later:
522 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
2343e117 523
eff5f524 524 # pre-5.30
2343e117 525 $c->req->args([qw/arg1 arg2 arg3/]);
526 $c->forward('/wherever');
527
eff5f524 528(See L<Catalyst::Manual::Intro#Flow_Control> for more information on
529passing arguments via C<forward>.)
530
822fe954 531=head2 Configure your application
532
533You configure your application with the C<config> method in your
534application class. This can be hard-coded, or brought in from a
535separate configuration file.
536
537=head3 Using YAML
538
539YAML is a method for creating flexible and readable configuration
540files. It's a great way to keep your Catalyst application configuration
541in one easy-to-understand location.
542
543In your application class (e.g. C<lib/MyApp.pm>):
544
545 use YAML;
546 # application setup
547 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
548 __PACKAGE__->setup;
549
550Now create C<myapp.yml> in your application home:
551
552 --- #YAML:1.0
553 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
554 name: MyApp
555
556 # authentication; perldoc Catalyst::Plugin::Authentication::CDBI
557 authentication:
558 user_class: 'MyApp::M::MyDB::Customer'
559 user_field: 'username'
560 password_field: 'password'
561 password_hash: 'md5'
562 role_class: 'MyApp::M::MyDB::Role'
563 user_role_class: 'MyApp::M::MyDB::PersonRole'
564 user_role_user_field: 'person'
565
566 # session; perldoc Catalyst::Plugin::Session::FastMmap
567 session:
568 expires: '3600'
569 rewrite: '0'
570 storage: '/tmp/myapp.session'
571
572 # emails; perldoc Catalyst::Plugin::Email
573 # this passes options as an array :(
574 email:
575 - SMTP
576 - localhost
577
578This is equivalent to:
579
580 # configure base package
581 __PACKAGE__->config( name => MyApp );
582 # configure authentication
583 __PACKAGE__->config->{authentication} = {
584 user_class => 'MyApp::M::MyDB::Customer',
585 ...
586 };
587 # configure sessions
588 __PACKAGE__->config->{session} = {
589 expires => 3600,
590 ...
591 };
592 # configure email sending
593 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
594
595See also L<YAML>.
596
3912ee04 597=head2 Using existing CDBI (etc.) classes with Catalyst
598
599Many people have existing Model classes that they would like to use with
600Catalyst (or, conversely, they want to write Catalyst models that can be
601used outside of Catalyst, e.g. in a cron job). It's trivial to write a
602simple component in Catalyst that slurps in an outside Model:
603
604 package MyApp::M::Catalog;
605 use base qw/Catalyst::Base Some::Other::CDBI::Module::Catalog/;
606 1;
607
608and that's it! Now C<Some::Other::CDBI::Module::Catalog> is part of your
609Cat app as C<MyApp::M::Catalog>.
610
6d23e1f4 611=head2 Delivering a Custom Error Page
f25a3283 612
6d23e1f4 613By default, Catalyst will display its own error page whenever it
614encounters an error in your application. When running under C<-Debug>
615mode, the error page is a useful screen including the error message and
616a full Data::Dumper output of the C<$c> context object. When not in
617C<-Debug>, users see a simple "Please come back later" screen.
f25a3283 618
26e73131 619To use a custom error page, use a special C<end> method to short-circuit
6d23e1f4 620the error processing. The following is an example; you might want to
621adjust it further depending on the needs of your application (for
622example, any calls to C<fillform> will probably need to go into this
623C<end> method; see L<Catalyst::Plugin::FillInForm>).
f25a3283 624
6d23e1f4 625 sub end : Private {
626 my ( $self, $c ) = @_;
627
628 if ( scalar @{ $c->error } ) {
629 $c->stash->{errors} = $c->error;
630 $c->stash->{template} = 'errors.tt';
631 $c->forward('MyApp::View::TT');
632 $c->{error} = [];
633 }
634
635 return 1 if $c->response->status =~ /^3\d\d$/;
636 return 1 if $c->response->body;
637
638 unless ( $c->response->content_type ) {
639 $c->response->content_type('text/html; charset=utf-8');
640 }
641
642 $c->forward('MyApp::View::TT');
643 }
f25a3283 644
6d23e1f4 645You can manually set errors in your code to trigger this page by calling
f25a3283 646
6d23e1f4 647 $c->error( 'You broke me!' );
f25a3283 648
fc7ec1d9 649=head1 AUTHOR
650
651Sebastian Riedel, C<sri@oook.de>
379ca371 652Danijel Milicevic, C<me@danijel.de>
653Viljo Marrandi, C<vilts@yahoo.com>
822fe954 654Marcus Ramberg, C<mramberg@cpan.org>
655Jesse Sheidlower, C<jester@panix.com>
379ca371 656Andy Grundman, C<andy@hybridized.org>
3912ee04 657Chisel Wright, C<pause@herlpacker.co.uk>
fc7ec1d9 658
659=head1 COPYRIGHT
660
61b1e958 661This program is free software, you can redistribute it and/or modify it
662under the same terms as Perl itself.