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 similiar 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->req->output("Your account has the role 'admin.'");
271 $c->req->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('?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 that
290 Catalyst will still execute the action defined in the URI (e.g. if you
291 tried to go to C</add>, then first 'begin' will forward to 'login', but after
292 that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any
293 actions that were to be called and forwards the user where we want him/her
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 OSX. For OSX 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; variations, some of
489 which could be simpler, are left as an exercise for the reader:
492 use lib qw(/var/www/MyApp/lib);
497 ServerName myapp.example.com
498 DocumentRoot /var/www/MyApp/root
500 SetHandler perl-script
503 <LocationMatch "/(static|favicon.ico)">
504 SetHandler default-handler
508 =head2 Forwarding with arguments
510 Sometimes you want to pass along arguments when forwarding to another
511 action. As of version 5.30, arguments can be passed in the call to
512 C<forward>; in earlier versions, you can manually set the arguments in
513 the Catalyst Request object:
515 # version 5.30 and later:
516 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
519 $c->req->args([qw/arg1 arg2 arg3/]);
520 $c->forward('/wherever');
522 (See L<Catalyst::Manual::Intro#Flow_Control> for more information on
523 passing arguments via C<forward>.)
525 =head2 Configure your application
527 You configure your application with the C<config> method in your
528 application class. This can be hard-coded, or brought in from a
529 separate configuration file.
533 YAML is a method for creating flexible and readable configuration
534 files. It's a great way to keep your Catalyst application configuration
535 in one easy-to-understand location.
537 In your application class (e.g. C<lib/MyApp.pm>):
541 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
544 Now create C<myapp.yml> in your application home:
547 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
550 # authentication; perldoc Catalyst::Plugin::Authentication::CDBI
552 user_class: 'MyApp::M::MyDB::Customer'
553 user_field: 'username'
554 password_field: 'password'
556 role_class: 'MyApp::M::MyDB::Role'
557 user_role_class: 'MyApp::M::MyDB::PersonRole'
558 user_role_user_field: 'person'
560 # session; perldoc Catalyst::Plugin::Session::FastMmap
564 storage: '/tmp/myapp.session'
566 # emails; perldoc Catalyst::Plugin::Email
567 # this passes options as an array :(
572 This is equivalent to:
574 # configure base package
575 __PACKAGE__->config( name => MyApp );
576 # configure authentication
577 __PACKAGE__->config->{authentication} = {
578 user_class => 'MyApp::M::MyDB::Customer',
582 __PACKAGE__->config->{session} = {
586 # configure email sending
587 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
593 Sebastian Riedel, C<sri@oook.de>
594 Danijel Milicevic, C<me@danijel.de>
595 Viljo Marrandi, C<vilts@yahoo.com>
596 Marcus Ramberg, C<mramberg@cpan.org>
597 Jesse Sheidlower, C<jester@panix.com>
598 Andy Grundman, C<andy@hybridized.org>
599 Marcus Ramberg C<mramberg@cpan.org>
600 Chisel Wright C<pause@herlpacker.co.uk>
604 This program is free software, you can redistribute it and/or modify it
605 under the same terms as Perl itself.