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
140 In this example, we'll use the
141 L<Catalyst::Plugin::Authentication::Store::DBIC> store and the
142 L<Catalyst::Plugin::Authentication::Credential::Password> credentials.
144 In the lib/MyApp.pm package, we'll need to change the C<use Catalyst;>
145 line to include the following modules:
150 Authentication::Store::DBIC
151 Authentication::Credential::Password
153 Session::Store::FastMmap
154 Session::State::Cookie
159 The Session, Session::Store::* and Session::State::* modules listed above
160 ensure that we stay logged-in across multiple page-views.
162 In our MyApp.yml configuration file, we'll need to add:
166 user_class: MyApp::Model::DBIC::User
168 password_field: password
169 password_type: hashed
170 password_hash_type: SHA-1
172 'user_class' is a DBIx::Class package for your users table.
173 'user_field' tells which field (column) is used for username lookup.
174 'password_field' is the password field in your table.
175 The above settings for 'password_type' and 'password_hash_type' ensure that
176 the password won't be stored in the database in clear text.
178 In SQLite, the users table might be something like:
181 id INTEGER PRIMARY KEY,
182 username VARCHAR(100),
183 password VARCHAR(100)
186 Now we need to create a DBIC::SchemaLoader component for this database
187 (changing "myapp.db" to wherever your SQLite database is).
189 script/myapp_create.pl model DBIC DBIC::SchemaLoader 'dbi:SQLite:myapp.db'
191 Now we can start creating our page controllers and templates.
192 For our homepage, we create the file "root/index.tt" containing:
197 <p>hello [% c.user.username %]</p>
198 <p><a href="[% c.uri_for( '/logout' ) %]">logout</a></p>
200 <p><a href="[% c.uri_for( '/login' ) %]">login</a></p>
205 If the user is logged in, they will be shown their name, and a logout link.
206 Otherwise, they will be shown a login link.
208 To display the homepage, we can uncomment the C<default> and C<end>
209 subroutines in lib/MyApp/Controller/Root.pm and populate them as so:
211 sub default : Private {
212 my ( $self, $c ) = @_;
214 $c->stash->{template} = 'index.tt';
218 my ( $self, $c ) = @_;
220 $c->forward( $c->view('TT') )
221 unless $c->response->body || $c->response->redirect;
224 The login template is very simple, as L<HTML::Widget> will handle the
225 HTML form creation for use. This is saved as "root/login.tt".
229 <link href="[% c.uri_for('/static/simple.css') %]" rel="stylesheet" type="text/css">
236 For the HTML form to look correct, we also copy the C<simple.css> file
237 from the L<HTML::Widget> distribution into our "root/static" folder.
238 This file is automatically server by the L<Catalyst::Plugin::Static::Simple>
239 module which we loaded in our lib/MyApp.pm package.
241 To handle login requests, we first create a controller, like so:
243 script/myapp_create.pl controller Login
245 In the lib/MyApp/Controller/Login.pm package, we can then uncomment the
246 C<default> subroutine, and populate it, as below.
248 First the widget is created, it needs the 'action' set, and 'username' and
249 'password' fields and a submit button added.
251 Then, if we've received a username and password in the request, we attempt
252 to login. If successful, we redirect to the homepage; if not the login form
253 will be displayed again.
255 sub default : Private {
256 my ( $self, $c ) = @_;
258 $c->widget->method('POST')->action( $c->uri_for('/login') );
259 $c->widget->element( 'Textfield', 'username' )->label( 'Username' );
260 $c->widget->element( 'Password', 'password' )->label( 'Password' );
261 $c->widget->element( 'Submit' )->value( 'Login' );
263 my $result = $c->widget->process( $c->req );
265 if ( my $user = $result->param('username')
266 and my $pass = $result->param('password') )
268 if ( $c->login( $user, $pass ) ) {
269 $c->response->redirect( $c->uri_for( "/" ) );
274 $c->stash->{template} = 'login.tt';
275 $c->stash->{result} = $result;
278 To handle logout's, we create a new controller:
280 script/myapp_create.pl controller Logout
282 Then in the lib/MyApp/Controller/Logout.pm package, we change the
283 C<default> subroutine, to logout and then redirect back to the
286 sub default : Private {
287 my ( $self, $c ) = @_;
291 $c->response->redirect( $c->uri_for( "/" ) );
294 Remember that to test this, we would first need to add a user to the
295 database, ensuring that the password field is saved as the SHA1 hash
296 of our desired password.
299 =head2 Pass-through login (and other actions)
301 An easy way of having assorted actions that occur during the processing
302 of a request that are orthogonal to its actual purpose - logins, silent
303 commands etc. Provide actions for these, but when they're required for
304 something else fill e.g. a form variable __login and have a sub begin
307 sub begin : Private {
309 foreach my $action (qw/login docommand foo bar whatever/) {
310 if ($c->req->params->{"__${action}"}) {
311 $c->forward($action);
316 =head2 How to use Catalyst without mod_perl
318 Catalyst applications give optimum performance when run under mod_perl.
319 However sometimes mod_perl is not an option, and running under CGI is
320 just too slow. There's also an alternative to mod_perl that gives
321 reasonable performance named FastCGI.
325 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
326 independent, scalable, extension to CGI that provides high performance
327 without the limitations of specific server APIs." Web server support
328 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
329 support in the C<FCGI> module. To convert a CGI Catalyst application
330 to FastCGI one needs to initialize an C<FCGI::Request> object and loop
331 while the C<Accept> method returns zero. The following code shows how
332 it is done - and it also works as a normal, single-shot CGI script.
339 my $request = FCGI::Request();
340 while ($request->Accept() >= 0) {
344 Any initialization code should be included outside the request-accept
347 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
348 complete HTTP response including the status line (e.g.:
350 FastCGI just wants a set of headers, so the sample code captures the
351 output and drops the first line if it is an HTTP status line (note:
354 The Apache C<mod_fastcgi> module is provided by a number of Linux
355 distro's and is straightforward to compile for most Unix-like systems.
356 The module provides a FastCGI Process Manager, which manages FastCGI
357 scripts. You configure your script as a FastCGI script with the
358 following Apache configuration directives:
361 AddHandler fastcgi-script fcgi
367 SetHandler fastcgi-script
368 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
371 C<mod_fastcgi> provides a number of options for controlling the FastCGI
372 scripts spawned; it also allows scripts to be run to handle the
373 authentication, authorization, and access check phases.
375 For more information see the FastCGI documentation, the C<FCGI> module
376 and L<http://www.fastcgi.com/>.
378 =head2 Serving static content
380 Serving static content in Catalyst can be somewhat tricky; this recipe
381 shows one possible solution. Using this recipe will serve all static
382 content through Catalyst when developing with the built-in HTTP::Daemon
383 server, and will make it easy to use Apache to serve the content when
384 your app goes into production.
386 Static content is best served from a single directory within your root
387 directory. Having many different directories such as C<root/css> and
388 C<root/images> requires more code to manage, because you must separately
389 identify each static directory--if you decide to add a C<root/js>
390 directory, you'll need to change your code to account for it. In
391 contrast, keeping all static directories as subdirectories of a main
392 C<root/static> directory makes things much easier to manager. Here's an
393 example of a typical root directory structure:
397 root/controller/stuff.tt
400 root/static/css/main.css
401 root/static/images/logo.jpg
402 root/static/js/code.js
405 All static content lives under C<root/static> with everything else being
406 Template Toolkit files. Now you can identify the static content by
407 matching C<static> from within Catalyst.
409 =head3 Serving with HTTP::Daemon (myapp_server.pl)
411 To serve these files under the standalone server, we first must load the
412 Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
415 In your main application class (MyApp.pm), load the plugin:
417 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
419 You will also need to make sure your end method does I<not> forward
420 static content to the view, perhaps like this:
423 my ( $self, $c ) = @_;
425 $c->forward( 'MyApp::V::TT' )
426 unless ( $c->res->body || !$c->stash->{template} );
429 This code will only forward to the view if a template has been
430 previously defined by a controller and if there is not already data in
431 C<$c-E<gt>res-E<gt>body>.
433 Next, create a controller to handle requests for the /static path. Use
434 the Helper to save time. This command will create a stub controller as
435 C<lib/MyApp/C/Static.pm>.
437 $ script/myapp_create.pl controller Static
439 Edit the file and add the following methods:
441 # serve all files under /static as static files
442 sub default : Path('/static') {
443 my ( $self, $c ) = @_;
445 # Optional, allow the browser to cache the content
446 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
448 $c->serve_static; # from Catalyst::Plugin::Static
451 # also handle requests for /favicon.ico
452 sub favicon : Path('/favicon.ico') {
453 my ( $self, $c ) = @_;
458 You can also define a different icon for the browser to use instead of
459 favicon.ico by using this in your HTML header:
461 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
463 =head3 Common problems
465 The Static plugin makes use of the C<shared-mime-info> package to
466 automatically determine MIME types. This package is notoriously
467 difficult to install, especially on win32 and OS X. For OS X the easiest
468 path might be to install Fink, then use C<apt-get install
469 shared-mime-info>. Restart the server, and everything should be fine.
471 Make sure you are using the latest version (>= 0.16) for best
472 results. If you are having errors serving CSS files, or if they get
473 served as text/plain instead of text/css, you may have an outdated
474 shared-mime-info version. You may also wish to simply use the following
475 code in your Static controller:
477 if ($c->req->path =~ /css$/i) {
478 $c->serve_static( "text/css" );
483 =head3 Serving with Apache
485 When using Apache, you can completely bypass Catalyst and the Static
486 controller by intercepting requests for the C<root/static> path at the
487 server level. All that is required is to define a DocumentRoot and add a
488 separate Location block for your static content. Here is a complete
489 config for this application under mod_perl 1.x:
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 And here's a simpler example that'll get you started:
510 Alias /static/ "/my/static/files/"
515 =head2 Forwarding with arguments
517 Sometimes you want to pass along arguments when forwarding to another
518 action. As of version 5.30, arguments can be passed in the call to
519 C<forward>; in earlier versions, you can manually set the arguments in
520 the Catalyst Request object:
522 # version 5.30 and later:
523 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
526 $c->req->args([qw/arg1 arg2 arg3/]);
527 $c->forward('/wherever');
529 (See the L<Catalyst::Manual::Intro> Flow_Control section for more
530 information on passing arguments via C<forward>.)
532 =head2 Configure your application
534 You configure your application with the C<config> method in your
535 application class. This can be hard-coded, or brought in from a
536 separate configuration file.
540 YAML is a method for creating flexible and readable configuration
541 files. It's a great way to keep your Catalyst application configuration
542 in one easy-to-understand location.
544 In your application class (e.g. C<lib/MyApp.pm>):
548 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
551 Now create C<myapp.yml> in your application home:
554 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
557 # session; perldoc Catalyst::Plugin::Session::FastMmap
561 storage: '/tmp/myapp.session'
563 # emails; perldoc Catalyst::Plugin::Email
564 # this passes options as an array :(
569 This is equivalent to:
571 # configure base package
572 __PACKAGE__->config( name => MyApp );
573 # configure authentication
574 __PACKAGE__->config->{authentication} = {
575 user_class => 'MyApp::M::MyDB::Customer',
579 __PACKAGE__->config->{session} = {
583 # configure email sending
584 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
588 =head2 Using existing DBIC (etc.) classes with Catalyst
590 Many people have existing Model classes that they would like to use with
591 Catalyst (or, conversely, they want to write Catalyst models that can be
592 used outside of Catalyst, e.g. in a cron job). It's trivial to write a
593 simple component in Catalyst that slurps in an outside Model:
595 package MyApp::Model::DB;
596 use base qw/Catalyst::Model::DBIC::Schema/;
598 schema_class => 'Some::DBIC::Schema',
599 connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}];
603 and that's it! Now C<Some::DBIC::Schema> is part of your
604 Cat app as C<MyApp::Model::DB>.
606 =head2 Delivering a Custom Error Page
608 By default, Catalyst will display its own error page whenever it
609 encounters an error in your application. When running under C<-Debug>
610 mode, the error page is a useful screen including the error message and
611 L<Data::Dump> output of the relevant parts of the C<$c> context object.
612 When not in C<-Debug>, users see a simple "Please come back later" screen.
614 To use a custom error page, use a special C<end> method to short-circuit
615 the error processing. The following is an example; you might want to
616 adjust it further depending on the needs of your application (for
617 example, any calls to C<fillform> will probably need to go into this
618 C<end> method; see L<Catalyst::Plugin::FillInForm>).
621 my ( $self, $c ) = @_;
623 if ( scalar @{ $c->error } ) {
624 $c->stash->{errors} = $c->error;
625 $c->stash->{template} = 'errors.tt';
626 $c->forward('MyApp::View::TT');
630 return 1 if $c->response->status =~ /^3\d\d$/;
631 return 1 if $c->response->body;
633 unless ( $c->response->content_type ) {
634 $c->response->content_type('text/html; charset=utf-8');
637 $c->forward('MyApp::View::TT');
640 You can manually set errors in your code to trigger this page by calling
642 $c->error( 'You broke me!' );
644 =head2 Require user logins
646 It's often useful to restrict access to your application to a set of
647 registered users, forcing everyone else to the login page until they're
650 To implement this in your application make sure you have a customer
651 table with username and password fields and a corresponding Model class
652 in your Catalyst application, then make the following changes:
658 Authentication::Store::DBIC
659 Authentication::Credential::Password
662 __PACKAGE__->config->{authentication}->{dbic} = {
663 'user_class' => 'My::Model::DBIC::User',
664 'user_field' => 'username',
665 'password_field' => 'password'
666 'password_type' => 'hashed',
667 'password_hash_type'=> 'SHA-1'
672 my $login_path = 'user/login';
674 # allow people to actually reach the login page!
675 if ($c->request->path eq $login_path) {
679 # if a user doesn't exist, force login
680 if ( !$c->user_exists ) {
681 # force the login screen to be shown
682 $c->response->redirect($c->request->base . $login_path);
685 # otherwise, we have a user - continue with the processing chain
689 =head3 lib/MyApp/C/User.pm
691 sub login : Path('/user/login') {
695 $c->stash->{'template'} = "user/login.tt";
696 # default form message
697 $c->stash->{'message'} = 'Please enter your username and password';
699 if ( $c->request->param('username') ) {
700 # try to log the user in
701 # login() is provided by ::Authentication::Credential::Password
703 $c->request->param('username'),
704 $c->request->param('password'),
707 # if login() returns 1, user is now logged in
708 $c->response->redirect('/some/page');
711 # otherwise we failed to login, try again!
712 $c->stash->{'message'} =
713 'Unable to authenticate the login details supplied';
717 sub logout : Path('/user/logout') {
722 # do the 'default' action
723 $c->response->redirect($c->request->base);
727 =head3 root/base/user/login.tt
729 [% INCLUDE header.tt %]
730 <form action="/user/login" method="POST" name="login_form">
732 <label for="username">username:</label><br />
733 <input type="text" id="username" name="username" /><br />
735 <label for="password">password:</label><br />
736 <input type="password" id="password" name="password" /><br />
738 <input type="submit" value="log in" name="form_submit" />
740 [% INCLUDE footer.tt %]
742 =head2 Role-based Authorization
744 For more advanced access control, you may want to consider using role-based
745 authorization. This means you can assign different roles to each user, e.g.
746 "user", "admin", etc.
748 The C<login> and C<logout> methods and view template are exactly the same as
749 in the previous example.
751 The L<Catalyst::Plugin::Authorization::Roles> plugin is required when
756 Authentication::Credential::Password
757 Authentication::Store::Htpasswd
761 Roles are implemented automatically when using
762 L<Catalyst::Authentication::Store::Htpasswd>:
764 # no additional role configuration required
765 __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
767 Or can be set up manually when using L<Catalyst::Authentication::Store::DBIC>:
769 # Authorization using a many-to-many role relationship
770 __PACKAGE__->config->{authorization}{dbic} = {
771 'role_class' => 'My::Model::DBIC::Role',
772 'role_field' => 'name',
773 'user_role_user_field' => 'user',
775 # DBIx::Class only (omit if using Class::DBI)
776 'role_rel' => 'user_role',
778 # Class::DBI only, (omit if using DBIx::Class)
779 'user_role_class' => 'My::Model::CDBI::UserRole'
780 'user_role_role_field' => 'role',
783 To restrict access to any action, you can use the C<check_user_roles> method:
785 sub restricted : Local {
786 my ( $self, $c ) = @_;
788 $c->detach("unauthorized")
789 unless $c->check_user_roles( "admin" );
791 # do something restricted here
794 You can also use the C<assert_user_roles> method. This just gives an error if
795 the current user does not have one of the required roles:
797 sub also_restricted : Global {
798 my ( $self, $c ) = @_;
799 $c->assert_user_roles( qw/ user admin / );
804 Sebastian Riedel, C<sri@oook.de>
805 Danijel Milicevic, C<me@danijel.de>
806 Viljo Marrandi, C<vilts@yahoo.com>
807 Marcus Ramberg, C<mramberg@cpan.org>
808 Jesse Sheidlower, C<jester@panix.com>
809 Andy Grundman, C<andy@hybridized.org>
810 Chisel Wright, C<pause@herlpacker.co.uk>
811 Will Hawes, C<info@whawes.co.uk>
812 Gavin Henry, C<ghenry@cpan.org> (Spell checking)
817 This program is free software, you can redistribute it and/or modify it
818 under the same terms as Perl itself.