Catalyst Tip: Generic Base Controllers

LTjake on 2006-11-28T03:32:58

So, you want to write a generic base controller for some set of actions? Great! Want to use Chained actions for nicer looking URLs? Fantastic!

Let's presume you're writing a generic account admin controller. Your URL-space might look like:

/admin/account/
account list
/admin/account/1/
account view
/admin/account/1/update
account update

Currently, you have a couple of options in order to make this a reality:

1) Action-specific configs

Catalyst's configurability is extremely granular -- you can set specific attributes as configuration parameters for any given action.

package MyApp::Controller::Admin::Account;

use strict;
use warnings;

use base 'Catalyst::Controller';

__PACKAGE__->config( actions => {
    instance => { PathPart => 'admin/account' },
    list     => { PathPart => 'admin/account' }
} );

sub list     : Chained('/') Args(0) { die "index of accounts" }
sub instance : Chained('/') CaptureArgs(1) { } # do something with $c->req->captures->[ 0 ]
sub view     : Chained('instance') PathPart('') Args(0) { die "view account" }
sub update   : Chained('instance') PathPart Args(0) { die "update account" }

1;

In the above example we're explicitly setting the PathPart for the list and instance actions -- However, it's not quite fully generic.

2) Create our own attribute

We'll create an attribute that will expose a controller's path_prefix to any action -- aptly named PathPrefix.

package MyApp::Controller::Admin::Account;

use strict;
use warnings;

use base 'Catalyst::Controller';

sub _parse_PathPrefix_attr { 
    my ( $self, $c, $name, $value ) = @_; 
    return PathPart => $self->path_prefix; 
} 

sub list     : Chained('/') PathPrefix Args(0) { die "index of accounts" }
sub instance : Chained('/') PathPrefix CaptureArgs(1) { } # do something with $c->req->captures->[ 0 ]
sub view     : Chained('instance') PathPart('') Args(0) { die "view account" }
sub update   : Chained('instance') PathPart Args(0) { die "update account" }

1;

Now that's better -- except for that pesky sub. Well, you're in luck -- PathPrefix is now in the current Catalyst::Runtime branch. Our code is now even simpler:

package MyApp::Controller::Admin::Account;

use strict;
use warnings;

use base 'Catalyst::Controller';

sub list     : Chained('/') PathPrefix Args(0) { die "index of accounts" }
sub instance : Chained('/') PathPrefix CaptureArgs(1) { } # do something with $c->req->captures->[ 0 ]
sub view     : Chained('instance') PathPart('') Args(0) { die "view account" }
sub update   : Chained('instance') PathPart Args(0) { die "update account" }

1;

As I've stated, Catalyst is extremely configurable. Let's say you don't like "admin/account" as the path? No Problem!

package MyApp::Controller::Admin::Account;

use strict;
use warnings;

use base 'Catalyst::Controller';

__PACKAGE__->config( path => 'foo/bar' );

sub list     : Chained('/') PathPrefix Args(0) { die "index of accounts" }
sub instance : Chained('/') PathPrefix CaptureArgs(1) { } # do something with $c->req->captures->[ 0 ]
sub view     : Chained('instance') PathPart('') Args(0) { die "view account" }
sub update   : Chained('instance') PathPart Args(0) { die "update account" }

1;

All of the actions will now start with "/foo/bar/"!

Happy Hacking.

Special thanks to mst for being my editor and sanity checker.