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.
44 The recommended way is to use Catalyst::Helper::Controller::Scaffold.
46 Just install this module, and to scaffold a Class::DBI Model class, do the following:
48 ./script/myapp_create controller <name> Scaffold <CDBI::Class>Scaffolding
55 =head3 Single file upload with Catalyst
57 To implement uploads in Catalyst, you need to have a HTML form similar to
60 <form action="/upload" method="post" enctype="multipart/form-data">
61 <input type="hidden" name="form_submit" value="yes">
62 <input type="file" name="my_file">
63 <input type="submit" value="Send">
66 It's very important not to forget C<enctype="multipart/form-data"> in
69 Catalyst Controller module 'upload' action:
74 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
76 if ( my $upload = $c->request->upload('my_file') ) {
78 my $filename = $upload->filename;
79 my $target = "/tmp/upload/$filename";
81 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
82 die( "Failed to copy '$filename' to '$target': $!" );
87 $c->stash->{template} = 'file_upload.html';
90 =head3 Multiple file upload with Catalyst
92 Code for uploading multiple files from one form needs a few changes:
94 The form should have this basic structure:
96 <form action="/upload" method="post" enctype="multipart/form-data">
97 <input type="hidden" name="form_submit" value="yes">
98 <input type="file" name="file1" size="50"><br>
99 <input type="file" name="file2" size="50"><br>
100 <input type="file" name="file3" size="50"><br>
101 <input type="submit" value="Send">
104 And in the controller:
109 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
111 for my $field ( $c->req->upload ) {
113 my $upload = $c->req->upload($field);
114 my $filename = $upload->filename;
115 my $target = "/tmp/upload/$filename";
117 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
118 die( "Failed to copy '$filename' to '$target': $!" );
123 $c->stash->{template} = 'file_upload.html';
126 C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
127 input fields and gets input names. After that is basic file saving code,
128 just like in single file upload.
130 Notice: C<die>ing might not be what you want to do, when an error
131 occurs, but it works as an example. A better idea would be to store
132 error C<$!> in $c->stash->{error} and show a custom error template
133 displaying this message.
135 For more information about uploads and usable methods look at
136 L<Catalyst::Request::Upload> and L<Catalyst::Request>.
138 =head2 Authentication with Catalyst::Plugin::Authentication::CDBI
140 There are (at least) two ways to implement authentication with this plugin:
141 1) only checking username and password;
142 2) checking username, password, and the roles the user has
144 For both variants you'll need the following code in your MyApp package:
146 use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
148 MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
149 user_field => 'email',
150 password_field => 'password' });
152 'user_class' is a Class::DBI class for your users table.
153 'user_field' tells which field is used for username lookup (might be
154 email, first name, surname etc.).
155 'password_field' is, well, password field in your table and by default
156 password is stored in plain text. Authentication::CDBI looks for 'user'
157 and 'password' fields in table, if they're not defined in the config.
159 In PostgreSQL, the users table might be something like:
164 surname varchar(100),
165 password varchar(100),
170 We'll discuss the first variant for now:
171 1. user:password login/auth without roles
173 To log in a user you might use an action like this:
177 if ($c->req->params->{username}) {
178 $c->session_login($c->req->params->{username},
179 $c->req->params->{password} );
180 if ($c->req->{user}) {
181 $c->forward('/restricted_area');
186 This action should not go in your MyApp class...if it does, it will
187 conflict with the built-in method of the same name. Instead, put it
188 in a Controller class.
190 $c->req->params->{username} and $c->req->params->{password} are html
191 form parameters from a login form. If login succeeds, then
192 $c->req->{user} contains the username of the authenticated user.
194 If you want to remember the user's login status in between further
195 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will
196 create a session id and session cookie and automatically append session
197 id to all urls. So all you have to do is just check $c->req->{user}
200 To log out a user, just call $c->session_logout.
202 Now let's take a look at the second variant:
203 2. user:password login/auth with roles
205 To use roles you need to add the following parameters to MyApp->config in the 'authentication' section:
207 role_class => 'MyApp::M::MyApp::Roles',
208 user_role_class => 'MyApp::M::MyApp::UserRoles',
209 user_role_user_field => 'user_id',
210 user_role_role_field => 'role_id',
212 Corresponding tables in PostgreSQL could look like this:
220 CREATE TABLE user_roles (
224 primary key(user_role_id),
225 foreign key(user_id) references users(user_id),
226 foreign key(role_id) references roles(role_id)
229 The 'roles' table is a list of role names and the 'user_role' table is
230 used for the user -> role lookup.
232 Now if a logged-in user wants to see a location which is allowed only
233 for people with an 'admin' role, in your controller you can check it
238 if ($c->roles(qw/admin/)) {
239 $c->res->output("Your account has the role 'admin.'");
241 $c->res->output("You're not allowed to be here.");
245 One thing you might need is to forward non-authenticated users to a login
246 form if they try to access restricted areas. If you want to do this
247 controller-wide (if you have one controller for your admin section) then it's
248 best to add a user check to a 'begin' action:
250 sub begin : Private {
252 unless ($c->req->{user}) {
253 $c->req->action(undef); ## notice this!!
254 $c->forward('/user/login');
258 Pay attention to $c->req->action(undef). This is needed because of the
259 way $c->forward works - C<forward> to C<login> gets called, but after
260 that Catalyst will still execute the action defined in the URI (e.g. if
261 you tried to go to C</add>, then first 'begin' will forward to 'login',
262 but after that 'add' will nonetheless be executed). So
263 $c->req->action(undef) undefines any actions that were to be called and
264 forwards the user where we want him/her to be.
266 And this is all you need to do.
268 =head2 Pass-through login (and other actions)
270 An easy way of having assorted actions that occur during the processing
271 of a request that are orthogonal to its actual purpose - logins, silent
272 commands etc. Provide actions for these, but when they're required for
273 something else fill e.g. a form variable __login and have a sub begin
276 sub begin : Private {
278 foreach my $action (qw/login docommand foo bar whatever/) {
279 if ($c->req->params->{"__${action}"}) {
280 $c->forward($action);
285 =head2 How to use Catalyst without mod_perl
287 Catalyst applications give optimum performance when run under mod_perl.
288 However sometimes mod_perl is not an option, and running under CGI is
289 just too slow. There's also an alternative to mod_perl that gives
290 reasonable performance named FastCGI.
294 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
295 independent, scalable, extension to CGI that provides high performance
296 without the limitations of specific server APIs." Web server support
297 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
298 support in the C<FCGI> module. To convert a CGI Catalyst application
299 to FastCGI one needs to initialize an C<FCGI::Request> object and loop
300 while the C<Accept> method returns zero. The following code shows how
301 it is done - and it also works as a normal, single-shot CGI script.
308 my $request = FCGI::Request();
309 while ($request->Accept() >= 0) {
313 Any initialization code should be included outside the request-accept
316 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
317 complete HTTP response including the status line (e.g.:
319 FastCGI just wants a set of headers, so the sample code captures the
320 output and drops the first line if it is an HTTP status line (note:
323 The Apache C<mod_fastcgi> module is provided by a number of Linux
324 distros and is straightforward to compile for most Unix-like systems.
325 The module provides a FastCGI Process Manager, which manages FastCGI
326 scripts. You configure your script as a FastCGI script with the
327 following Apache configuration directives:
330 AddHandler fastcgi-script fcgi
336 SetHandler fastcgi-script
337 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
340 C<mod_fastcgi> provides a number of options for controlling the FastCGI
341 scripts spawned; it also allows scripts to be run to handle the
342 authentication, authorization, and access check phases.
344 For more information see the FastCGI documentation, the C<FCGI> module
345 and L<http://www.fastcgi.com/>.
347 =head2 Serving static content
349 Serving static content in Catalyst can be somewhat tricky; this recipe
350 shows one possible solution. Using this recipe will serve all static
351 content through Catalyst when developing with the built-in HTTP::Daemon
352 server, and will make it easy to use Apache to serve the content when
353 your app goes into production.
355 Static content is best served from a single directory within your root
356 directory. Having many different directories such as C<root/css> and
357 C<root/images> requires more code to manage, because you must separately
358 identify each static directory--if you decide to add a C<root/js>
359 directory, you'll need to change your code to account for it. In
360 contrast, keeping all static directories as subdirectories of a main
361 C<root/static> directory makes things much easier to manager. Here's an
362 example of a typical root directory structure:
366 root/controller/stuff.tt
369 root/static/css/main.css
370 root/static/images/logo.jpg
371 root/static/js/code.js
374 All static content lives under C<root/static> with everything else being
375 Template Toolkit files. Now you can identify the static content by
376 matching C<static> from within Catalyst.
378 =head3 Serving with HTTP::Daemon (myapp_server.pl)
380 To serve these files under the standalone server, we first must load the
381 Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
384 In your main application class (MyApp.pm), load the plugin:
386 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
388 You will also need to make sure your end method does I<not> forward
389 static content to the view, perhaps like this:
392 my ( $self, $c ) = @_;
394 $c->forward( 'MyApp::V::TT' )
395 unless ( $c->res->body || !$c->stash->{template} );
398 This code will only forward to the view if a template has been
399 previously defined by a controller and if there is not already data in
400 C<$c-E<gt>res-E<gt>body>.
402 Next, create a controller to handle requests for the /static path. Use
403 the Helper to save time. This command will create a stub controller as
404 C<lib/MyApp/C/Static.pm>.
406 $ script/myapp_create.pl controller Static
408 Edit the file and add the following methods:
410 # serve all files under /static as static files
411 sub default : Path('/static') {
412 my ( $self, $c ) = @_;
414 # Optional, allow the browser to cache the content
415 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
417 $c->serve_static; # from Catalyst::Plugin::Static
420 # also handle requests for /favicon.ico
421 sub favicon : Path('/favicon.ico') {
422 my ( $self, $c ) = @_;
427 You can also define a different icon for the browser to use instead of
428 favicon.ico by using this in your HTML header:
430 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
432 =head3 Common problems
434 The Static plugin makes use of the C<shared-mime-info> package to
435 automatically determine MIME types. This package is notoriously
436 difficult to install, especially on win32 and OS X. For OS X the easiest
437 path might be to install Fink, then use C<apt-get install
438 shared-mime-info>. Restart the server, and everything should be fine.
440 Make sure you are using the latest version (>= 0.16) for best
441 results. If you are having errors serving CSS files, or if they get
442 served as text/plain instead of text/css, you may have an outdated
443 shared-mime-info version. You may also wish to simply use the following
444 code in your Static controller:
446 if ($c->req->path =~ /css$/i) {
447 $c->serve_static( "text/css" );
452 =head3 Serving with Apache
454 When using Apache, you can completely bypass Catalyst and the Static
455 controller by intercepting requests for the C<root/static> path at the
456 server level. All that is required is to define a DocumentRoot and add a
457 separate Location block for your static content. Here is a complete
458 config for this application under mod_perl 1.x:
461 use lib qw(/var/www/MyApp/lib);
466 ServerName myapp.example.com
467 DocumentRoot /var/www/MyApp/root
469 SetHandler perl-script
472 <LocationMatch "/(static|favicon.ico)">
473 SetHandler default-handler
477 And here's a simpler example that'll get you started:
479 Alias /static/ "/my/static/files/"
484 =head2 Forwarding with arguments
486 Sometimes you want to pass along arguments when forwarding to another
487 action. As of version 5.30, arguments can be passed in the call to
488 C<forward>; in earlier versions, you can manually set the arguments in
489 the Catalyst Request object:
491 # version 5.30 and later:
492 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
495 $c->req->args([qw/arg1 arg2 arg3/]);
496 $c->forward('/wherever');
498 (See L<Catalyst::Manual::Intro#Flow_Control> for more information on
499 passing arguments via C<forward>.)
501 =head2 Configure your application
503 You configure your application with the C<config> method in your
504 application class. This can be hard-coded, or brought in from a
505 separate configuration file.
509 YAML is a method for creating flexible and readable configuration
510 files. It's a great way to keep your Catalyst application configuration
511 in one easy-to-understand location.
513 In your application class (e.g. C<lib/MyApp.pm>):
517 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
520 Now create C<myapp.yml> in your application home:
523 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
526 # authentication; perldoc Catalyst::Plugin::Authentication::CDBI
528 user_class: 'MyApp::M::MyDB::Customer'
529 user_field: 'username'
530 password_field: 'password'
532 role_class: 'MyApp::M::MyDB::Role'
533 user_role_class: 'MyApp::M::MyDB::PersonRole'
534 user_role_user_field: 'person'
536 # session; perldoc Catalyst::Plugin::Session::FastMmap
540 storage: '/tmp/myapp.session'
542 # emails; perldoc Catalyst::Plugin::Email
543 # this passes options as an array :(
548 This is equivalent to:
550 # configure base package
551 __PACKAGE__->config( name => MyApp );
552 # configure authentication
553 __PACKAGE__->config->{authentication} = {
554 user_class => 'MyApp::M::MyDB::Customer',
558 __PACKAGE__->config->{session} = {
562 # configure email sending
563 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
567 =head2 Using existing CDBI (etc.) classes with Catalyst
569 Many people have existing Model classes that they would like to use with
570 Catalyst (or, conversely, they want to write Catalyst models that can be
571 used outside of Catalyst, e.g. in a cron job). It's trivial to write a
572 simple component in Catalyst that slurps in an outside Model:
574 package MyApp::M::Catalog;
575 use base qw/Catalyst::Base Some::Other::CDBI::Module::Catalog/;
578 and that's it! Now C<Some::Other::CDBI::Module::Catalog> is part of your
579 Cat app as C<MyApp::M::Catalog>.
581 =head2 Delivering a Custom Error Page
583 By default, Catalyst will display its own error page whenever it
584 encounters an error in your application. When running under C<-Debug>
585 mode, the error page is a useful screen including the error message and
586 a full Data::Dumper output of the C<$c> context object. When not in
587 C<-Debug>, users see a simple "Please come back later" screen.
589 To use a custom error page, use a special C<end> method to short-circuit
590 the error processing. The following is an example; you might want to
591 adjust it further depending on the needs of your application (for
592 example, any calls to C<fillform> will probably need to go into this
593 C<end> method; see L<Catalyst::Plugin::FillInForm>).
596 my ( $self, $c ) = @_;
598 if ( scalar @{ $c->error } ) {
599 $c->stash->{errors} = $c->error;
600 $c->stash->{template} = 'errors.tt';
601 $c->forward('MyApp::View::TT');
605 return 1 if $c->response->status =~ /^3\d\d$/;
606 return 1 if $c->response->body;
608 unless ( $c->response->content_type ) {
609 $c->response->content_type('text/html; charset=utf-8');
612 $c->forward('MyApp::View::TT');
615 You can manually set errors in your code to trigger this page by calling
617 $c->error( 'You broke me!' );
621 Sebastian Riedel, C<sri@oook.de>
622 Danijel Milicevic, C<me@danijel.de>
623 Viljo Marrandi, C<vilts@yahoo.com>
624 Marcus Ramberg, C<mramberg@cpan.org>
625 Jesse Sheidlower, C<jester@panix.com>
626 Andy Grundman, C<andy@hybridized.org>
627 Chisel Wright, C<pause@herlpacker.co.uk>
631 This program is free software, you can redistribute it and/or modify it
632 under the same terms as Perl itself.