Changed credits section
[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
87To implement uploads in Catalyst you need to have a HTML form similiar to
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}) {
211 $c->forward('?restricted_area');
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/)) {
269 $c->req->output("Your account has the role 'admin.'");
270 } else {
51ef2818 271 $c->req->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
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!!
284 $c->forward('?login');
285 }
61b1e958 286 }
deb90705 287
51ef2818 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 that
290Catalyst will still execute the action defined in the URI (e.g. if you
291tried to go to C</add>, then first 'begin' will forward to 'login', but after
292that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any
293actions that were to be called and forwards the user where we want him/her
deb90705 294to be.
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
466difficult to install, especially on win32 and OSX. For OSX the easiest
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
488config for this application under mod_perl 1.x; variations, some of
489which could be simpler, are left as an exercise for the reader:
490
491 <Perl>
492 use lib qw(/var/www/MyApp/lib);
493 </Perl>
494 PerlModule MyApp
495
496 <VirtualHost *>
497 ServerName myapp.example.com
498 DocumentRoot /var/www/MyApp/root
499 <Location />
500 SetHandler perl-script
501 PerlHandler MyApp
502 </Location>
503 <LocationMatch "/(static|favicon.ico)">
504 SetHandler default-handler
505 </LocationMatch>
506 </VirtualHost>
507
508=head2 Forwarding with arguments
145074c2 509
eff5f524 510Sometimes you want to pass along arguments when forwarding to another
511action. As of version 5.30, arguments can be passed in the call to
512C<forward>; in earlier versions, you can manually set the arguments in
513the Catalyst Request object:
e6394847 514
eff5f524 515 # version 5.30 and later:
516 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
2343e117 517
eff5f524 518 # pre-5.30
2343e117 519 $c->req->args([qw/arg1 arg2 arg3/]);
520 $c->forward('/wherever');
521
eff5f524 522(See L<Catalyst::Manual::Intro#Flow_Control> for more information on
523passing arguments via C<forward>.)
524
822fe954 525=head2 Configure your application
526
527You configure your application with the C<config> method in your
528application class. This can be hard-coded, or brought in from a
529separate configuration file.
530
531=head3 Using YAML
532
533YAML is a method for creating flexible and readable configuration
534files. It's a great way to keep your Catalyst application configuration
535in one easy-to-understand location.
536
537In your application class (e.g. C<lib/MyApp.pm>):
538
539 use YAML;
540 # application setup
541 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
542 __PACKAGE__->setup;
543
544Now create C<myapp.yml> in your application home:
545
546 --- #YAML:1.0
547 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
548 name: MyApp
549
550 # authentication; perldoc Catalyst::Plugin::Authentication::CDBI
551 authentication:
552 user_class: 'MyApp::M::MyDB::Customer'
553 user_field: 'username'
554 password_field: 'password'
555 password_hash: 'md5'
556 role_class: 'MyApp::M::MyDB::Role'
557 user_role_class: 'MyApp::M::MyDB::PersonRole'
558 user_role_user_field: 'person'
559
560 # session; perldoc Catalyst::Plugin::Session::FastMmap
561 session:
562 expires: '3600'
563 rewrite: '0'
564 storage: '/tmp/myapp.session'
565
566 # emails; perldoc Catalyst::Plugin::Email
567 # this passes options as an array :(
568 email:
569 - SMTP
570 - localhost
571
572This is equivalent to:
573
574 # configure base package
575 __PACKAGE__->config( name => MyApp );
576 # configure authentication
577 __PACKAGE__->config->{authentication} = {
578 user_class => 'MyApp::M::MyDB::Customer',
579 ...
580 };
581 # configure sessions
582 __PACKAGE__->config->{session} = {
583 expires => 3600,
584 ...
585 };
586 # configure email sending
587 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
588
589See also L<YAML>.
590
3912ee04 591=head2 Using existing CDBI (etc.) classes with Catalyst
592
593Many people have existing Model classes that they would like to use with
594Catalyst (or, conversely, they want to write Catalyst models that can be
595used outside of Catalyst, e.g. in a cron job). It's trivial to write a
596simple component in Catalyst that slurps in an outside Model:
597
598 package MyApp::M::Catalog;
599 use base qw/Catalyst::Base Some::Other::CDBI::Module::Catalog/;
600 1;
601
602and that's it! Now C<Some::Other::CDBI::Module::Catalog> is part of your
603Cat app as C<MyApp::M::Catalog>.
604
fc7ec1d9 605=head1 AUTHOR
606
607Sebastian Riedel, C<sri@oook.de>
379ca371 608Danijel Milicevic, C<me@danijel.de>
609Viljo Marrandi, C<vilts@yahoo.com>
822fe954 610Marcus Ramberg, C<mramberg@cpan.org>
611Jesse Sheidlower, C<jester@panix.com>
379ca371 612Andy Grundman, C<andy@hybridized.org>
3912ee04 613Chisel Wright, C<pause@herlpacker.co.uk>
fc7ec1d9 614
615=head1 COPYRIGHT
616
61b1e958 617This program is free software, you can redistribute it and/or modify it
618under the same terms as Perl itself.