3 Catalyst::Manual::Cookbook - Cooking with Catalyst
7 Yummy code like your mum used to bake!
11 =head2 Force debug screen
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.
17 my ( $self, $c ) = @_;
21 If you're tired of removing and adding this all the time, you can add a
22 condition in the C<end> action. For example:
25 my ( $self, $c ) = @_;
26 die "forced debug" if $c->req->params->{dump_info};
29 Then just add to your query string C<"&dump_info=1">, or the like, to
33 =head2 Disable statistics
35 Just add this line to your application class if you don't want those nifty
36 statistics in your debug messages.
38 sub Catalyst::Log::info { }
42 Scaffolding is very simple with Catalyst.
43 Just use Catalyst::Model::CDBI::CRUD as your base class.
45 # lib/MyApp/Model/CDBI.pm
46 package MyApp::Model::CDBI;
49 use base 'Catalyst::Model::CDBI::CRUD';
52 dsn => 'dbi:SQLite:/tmp/myapp.db',
61 use Catalyst 'FormValidator';
64 name => 'My Application',
65 root => '/home/joeuser/myapp/root'
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');
76 Modify the C<$c-E<gt>form()> parameters to match your needs, and don't
77 forget to copy the templates into the template root. Can't find the
78 templates? They were in the CRUD model distribution, so you can do
79 B<look Catalyst::Model::CDBI::CRUD> from the CPAN shell to find them.
81 Other Scaffolding modules are in development at the time of writing.
85 =head3 Single file upload with Catalyst
87 To implement uploads in Catalyst, you need to have a HTML form similar to
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">
96 It's very important not to forget C<enctype="multipart/form-data"> in
99 Catalyst Controller module 'upload' action:
101 sub upload : Global {
104 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
106 if ( my $upload = $c->request->upload('my_file') ) {
108 my $filename = $upload->filename;
109 my $target = "/tmp/upload/$filename";
111 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
112 die( "Failed to copy '$filename' to '$target': $!" );
117 $c->stash->{template} = 'file_upload.html';
120 =head3 Multiple file upload with Catalyst
122 Code for uploading multiple files from one form needs a few changes:
124 The form should have this basic structure:
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">
134 And in the controller:
139 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
141 for my $field ( $c->req->upload ) {
143 my $upload = $c->req->upload($field);
144 my $filename = $upload->filename;
145 my $target = "/tmp/upload/$filename";
147 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
148 die( "Failed to copy '$filename' to '$target': $!" );
153 $c->stash->{template} = 'file_upload.html';
156 C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
157 input fields and gets input names. After that is basic file saving code,
158 just like in single file upload.
160 Notice: C<die>ing might not be what you want to do, when an error
161 occurs, but it works as an example. A better idea would be to store
162 error C<$!> in $c->stash->{error} and show a custom error template
163 displaying this message.
165 For more information about uploads and usable methods look at
166 L<Catalyst::Request::Upload> and L<Catalyst::Request>.
168 =head2 Authentication with Catalyst::Plugin::Authentication::CDBI
170 There are (at least) two ways to implement authentication with this plugin:
171 1) only checking username and password;
172 2) checking username, password, and the roles the user has
174 For both variants you'll need the following code in your MyApp package:
176 use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
178 MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
179 user_field => 'email',
180 password_field => 'password' });
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
184 email, first name, surname etc.).
185 'password_field' is, well, password field in your table and by default
186 password is stored in plain text. Authentication::CDBI looks for 'user'
187 and 'password' fields in table, if they're not defined in the config.
189 In PostgreSQL, the users table might be something like:
194 surname varchar(100),
195 password varchar(100),
200 We'll discuss the first variant for now:
201 1. user:password login/auth without roles
203 To log in a user you might use an action like this:
207 if ($c->req->params->{username}) {
208 $c->session_login($c->req->params->{username},
209 $c->req->params->{password} );
210 if ($c->req->{user}) {
211 $c->forward('/restricted_area');
216 This action should not go in your MyApp class...if it does, it will
217 conflict with the built-in method of the same name. Instead, put it
218 in a Controller class.
220 $c->req->params->{username} and $c->req->params->{password} are html
221 form parameters from a login form. If login succeeds, then
222 $c->req->{user} contains the username of the authenticated user.
224 If you want to remember the user's login status in between further
225 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will
226 create a session id and session cookie and automatically append session
227 id to all urls. So all you have to do is just check $c->req->{user}
230 To log out a user, just call $c->session_logout.
232 Now let's take a look at the second variant:
233 2. user:password login/auth with roles
235 To use roles you need to add the following parameters to MyApp->config in the 'authentication' section:
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',
242 Corresponding tables in PostgreSQL could look like this:
250 CREATE TABLE user_roles (
254 primary key(user_role_id),
255 foreign key(user_id) references users(user_id),
256 foreign key(role_id) references roles(role_id)
259 The 'roles' table is a list of role names and the 'user_role' table is
260 used for the user -> role lookup.
262 Now if a logged-in user wants to see a location which is allowed only
263 for people with an 'admin' role, in your controller you can check it
268 if ($c->roles(qw/admin/)) {
269 $c->res->output("Your account has the role 'admin.'");
271 $c->res->output("You're not allowed to be here.");
275 One thing you might need is to forward non-authenticated users to a login
276 form if they try to access restricted areas. If you want to do this
277 controller-wide (if you have one controller for your admin section) then it's
278 best to add a user check to a 'begin' action:
280 sub begin : Private {
282 unless ($c->req->{user}) {
283 $c->req->action(undef); ## notice this!!
284 $c->forward('/user/login');
288 Pay attention to $c->req->action(undef). This is needed because of the
289 way $c->forward works - C<forward> to C<login> gets called, but after
290 that Catalyst will still execute the action defined in the URI (e.g. if
291 you tried to go to C</add>, then first 'begin' will forward to 'login',
292 but after that 'add' will nonetheless be executed). So
293 $c->req->action(undef) undefines any actions that were to be called and
294 forwards the user where we want him/her to be.
296 And this is all you need to do.
298 =head2 Pass-through login (and other actions)
300 An easy way of having assorted actions that occur during the processing
301 of a request that are orthogonal to its actual purpose - logins, silent
302 commands etc. Provide actions for these, but when they're required for
303 something else fill e.g. a form variable __login and have a sub begin
306 sub begin : Private {
308 foreach my $action (qw/login docommand foo bar whatever/) {
309 if ($c->req->params->{"__${action}"}) {
310 $c->forward($action);
315 =head2 How to use Catalyst without mod_perl
317 Catalyst applications give optimum performance when run under mod_perl.
318 However sometimes mod_perl is not an option, and running under CGI is
319 just too slow. There's also an alternative to mod_perl that gives
320 reasonable performance named FastCGI.
324 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
325 independent, scalable, extension to CGI that provides high performance
326 without the limitations of specific server APIs." Web server support
327 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
328 support in the C<FCGI> module. To convert a CGI Catalyst application
329 to FastCGI one needs to initialize an C<FCGI::Request> object and loop
330 while the C<Accept> method returns zero. The following code shows how
331 it is done - and it also works as a normal, single-shot CGI script.
338 my $request = FCGI::Request();
339 while ($request->Accept() >= 0) {
343 Any initialization code should be included outside the request-accept
346 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
347 complete HTTP response including the status line (e.g.:
349 FastCGI just wants a set of headers, so the sample code captures the
350 output and drops the first line if it is an HTTP status line (note:
353 The Apache C<mod_fastcgi> module is provided by a number of Linux
354 distros and is straightforward to compile for most Unix-like systems.
355 The module provides a FastCGI Process Manager, which manages FastCGI
356 scripts. You configure your script as a FastCGI script with the
357 following Apache configuration directives:
360 AddHandler fastcgi-script fcgi
366 SetHandler fastcgi-script
367 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
370 C<mod_fastcgi> provides a number of options for controlling the FastCGI
371 scripts spawned; it also allows scripts to be run to handle the
372 authentication, authorization, and access check phases.
374 For more information see the FastCGI documentation, the C<FCGI> module
375 and L<http://www.fastcgi.com/>.
377 =head2 Serving static content
379 Serving static content in Catalyst can be somewhat tricky; this recipe
380 shows one possible solution. Using this recipe will serve all static
381 content through Catalyst when developing with the built-in HTTP::Daemon
382 server, and will make it easy to use Apache to serve the content when
383 your app goes into production.
385 Static content is best served from a single directory within your root
386 directory. Having many different directories such as C<root/css> and
387 C<root/images> requires more code to manage, because you must separately
388 identify each static directory--if you decide to add a C<root/js>
389 directory, you'll need to change your code to account for it. In
390 contrast, keeping all static directories as subdirectories of a main
391 C<root/static> directory makes things much easier to manager. Here's an
392 example of a typical root directory structure:
396 root/controller/stuff.tt
399 root/static/css/main.css
400 root/static/images/logo.jpg
401 root/static/js/code.js
404 All static content lives under C<root/static> with everything else being
405 Template Toolkit files. Now you can identify the static content by
406 matching C<static> from within Catalyst.
408 =head3 Serving with HTTP::Daemon (myapp_server.pl)
410 To serve these files under the standalone server, we first must load the
411 Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
414 In your main application class (MyApp.pm), load the plugin:
416 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
418 You will also need to make sure your end method does I<not> forward
419 static content to the view, perhaps like this:
422 my ( $self, $c ) = @_;
424 $c->forward( 'MyApp::V::TT' )
425 unless ( $c->res->body || !$c->stash->{template} );
428 This code will only forward to the view if a template has been
429 previously defined by a controller and if there is not already data in
430 C<$c-E<gt>res-E<gt>body>.
432 Next, create a controller to handle requests for the /static path. Use
433 the Helper to save time. This command will create a stub controller as
434 C<lib/MyApp/C/Static.pm>.
436 $ script/myapp_create.pl controller Static
438 Edit the file and add the following methods:
440 # serve all files under /static as static files
441 sub default : Path('/static') {
442 my ( $self, $c ) = @_;
444 # Optional, allow the browser to cache the content
445 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
447 $c->serve_static; # from Catalyst::Plugin::Static
450 # also handle requests for /favicon.ico
451 sub favicon : Path('/favicon.ico') {
452 my ( $self, $c ) = @_;
457 You can also define a different icon for the browser to use instead of
458 favicon.ico by using this in your HTML header:
460 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
462 =head3 Common problems
464 The Static plugin makes use of the C<shared-mime-info> package to
465 automatically determine MIME types. This package is notoriously
466 difficult to install, especially on win32 and OS X. For OS X the easiest
467 path might be to install Fink, then use C<apt-get install
468 shared-mime-info>. Restart the server, and everything should be fine.
470 Make sure you are using the latest version (>= 0.16) for best
471 results. If you are having errors serving CSS files, or if they get
472 served as text/plain instead of text/css, you may have an outdated
473 shared-mime-info version. You may also wish to simply use the following
474 code in your Static controller:
476 if ($c->req->path =~ /css$/i) {
477 $c->serve_static( "text/css" );
482 =head3 Serving with Apache
484 When using Apache, you can completely bypass Catalyst and the Static
485 controller by intercepting requests for the C<root/static> path at the
486 server level. All that is required is to define a DocumentRoot and add a
487 separate Location block for your static content. Here is a complete
488 config for this application under mod_perl 1.x:
491 use lib qw(/var/www/MyApp/lib);
496 ServerName myapp.example.com
497 DocumentRoot /var/www/MyApp/root
499 SetHandler perl-script
502 <LocationMatch "/(static|favicon.ico)">
503 SetHandler default-handler
507 And here's a simpler example that'll get you started:
509 Alias /static/ "/my/static/files/"
514 =head2 Forwarding with arguments
516 Sometimes you want to pass along arguments when forwarding to another
517 action. As of version 5.30, arguments can be passed in the call to
518 C<forward>; in earlier versions, you can manually set the arguments in
519 the Catalyst Request object:
521 # version 5.30 and later:
522 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
525 $c->req->args([qw/arg1 arg2 arg3/]);
526 $c->forward('/wherever');
528 (See L<Catalyst::Manual::Intro#Flow_Control> for more information on
529 passing arguments via C<forward>.)
531 =head2 Configure your application
533 You configure your application with the C<config> method in your
534 application class. This can be hard-coded, or brought in from a
535 separate configuration file.
539 YAML is a method for creating flexible and readable configuration
540 files. It's a great way to keep your Catalyst application configuration
541 in one easy-to-understand location.
543 In your application class (e.g. C<lib/MyApp.pm>):
547 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
550 Now create C<myapp.yml> in your application home:
553 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
556 # authentication; perldoc Catalyst::Plugin::Authentication::CDBI
558 user_class: 'MyApp::M::MyDB::Customer'
559 user_field: 'username'
560 password_field: 'password'
562 role_class: 'MyApp::M::MyDB::Role'
563 user_role_class: 'MyApp::M::MyDB::PersonRole'
564 user_role_user_field: 'person'
566 # session; perldoc Catalyst::Plugin::Session::FastMmap
570 storage: '/tmp/myapp.session'
572 # emails; perldoc Catalyst::Plugin::Email
573 # this passes options as an array :(
578 This is equivalent to:
580 # configure base package
581 __PACKAGE__->config( name => MyApp );
582 # configure authentication
583 __PACKAGE__->config->{authentication} = {
584 user_class => 'MyApp::M::MyDB::Customer',
588 __PACKAGE__->config->{session} = {
592 # configure email sending
593 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
597 =head2 Using existing CDBI (etc.) classes with Catalyst
599 Many people have existing Model classes that they would like to use with
600 Catalyst (or, conversely, they want to write Catalyst models that can be
601 used outside of Catalyst, e.g. in a cron job). It's trivial to write a
602 simple component in Catalyst that slurps in an outside Model:
604 package MyApp::M::Catalog;
605 use base qw/Catalyst::Base Some::Other::CDBI::Module::Catalog/;
608 and that's it! Now C<Some::Other::CDBI::Module::Catalog> is part of your
609 Cat app as C<MyApp::M::Catalog>.
611 =head2 Delivering a Custom Error Page
613 By default, Catalyst will display its own error page whenever it
614 encounters an error in your application. When running under C<-Debug>
615 mode, the error page is a useful screen including the error message and
616 a full Data::Dumper output of the C<$c> context object. When not in
617 C<-Debug>, users see a simple "Please come back later" screen.
619 To use a custom error page, use a special C<end> method to short-circuit
620 the error processing. The following is an example; you might want to
621 adjust it further depending on the needs of your application (for
622 example, any calls to C<fillform> will probably need to go into this
623 C<end> method; see L<Catalyst::Plugin::FillInForm>).
626 my ( $self, $c ) = @_;
628 if ( scalar @{ $c->error } ) {
629 $c->stash->{errors} = $c->error;
630 $c->stash->{template} = 'errors.tt';
631 $c->forward('MyApp::View::TT');
635 return 1 if $c->response->status =~ /^3\d\d$/;
636 return 1 if $c->response->body;
638 unless ( $c->response->content_type ) {
639 $c->response->content_type('text/html; charset=utf-8');
642 $c->forward('MyApp::View::TT');
645 You can manually set errors in your code to trigger this page by calling
647 $c->error( 'You broke me!' );
651 Sebastian Riedel, C<sri@oook.de>
652 Danijel Milicevic, C<me@danijel.de>
653 Viljo Marrandi, C<vilts@yahoo.com>
654 Marcus Ramberg, C<mramberg@cpan.org>
655 Jesse Sheidlower, C<jester@panix.com>
656 Andy Grundman, C<andy@hybridized.org>
657 Chisel Wright, C<pause@herlpacker.co.uk>
661 This program is free software, you can redistribute it and/or modify it
662 under the same terms as Perl itself.