Background:
I'm using a CRUD framework in Catalyst that auto-generates forms and lists for all tables in a given database. For example: /admin/list/person or /admin/add/person or /admin/edit/person/3 all dynamically generate pages or forms as appropriate for the table 'person'. (In other words, Admin.pm has actions edit, list, add, delete and so on that expect a table argument and possibly a row-identifying argument.)
Question:
In the particular application I'm building, the database will be used by multiple customers, so I want to introduce a URI scheme where the first element is the customer's identifier, followed by the administrative action/table etc:
- /cust1/admin/list/person
- /cust2/admin/add/person
- /cust2/admin/edit/person/3
This is for "branding" purposes, and also to ensure that bookmarks or URLs passed from one user to another do the expected thing.
But I'm having a lot of trouble getting this to work. I would prefer not to have to modify the subs in the existing framework, so I've been trying variations on the following:
sub customer : Regex('^(\w+)/(admin)$') {
my ($self, $c, @args) = @_;
#validation of captured arg snipped..
my $path = join('/', 'admin', @args);
$c->request->path($path);
$c->dispatcher->prepare_action($c);
$c->forward($c->action, $c->req->args);
}
But it just will not behave. I've used regex matching actions many times, but putting one in the very first 'barrel' of a URI seems 开发者_开发百科unusually traumatic. Any suggestions gratefully received.
Make the customer action :Chained and then chain the admin action to that one. For example:
sub customer :Chained('/') :PathPart('') :CaptureArgs(1) {
# load current customer into the stash
}
sub admin :Chained('customer') :PathPart('admin') :Args(0) {
}
For more information see the Catalyst::DispatchType::Chained
I've marked Dimitar's answer as the correct one, because it put me on the right track to solving this problem. (I've never really had a need for - or grokked FTM - Chained actions until now.)
Here's what I've got in the various controllers, for anyone who's interested.
=== Root.pm ===
sub customer_home : Path: Args(1) { # eg /cust1
my ($self, $c, $custarg) = @_;
...
}
sub ourclub :Chained('/') :PathPart('') :CaptureArgs(1) { # eg /cust1/<admin-function>
my ($self, $c, $custarg) = @_;
...
}
=== Admin.pm ===
use base 'Framework::Controller';
# create chained action versions of the framework actions
sub admin :Chained('/customer') :PathPart('admin') { shift->SUPER::admin(@_) }
sub list :Chained('/customer') :PathPart('list') { shift->SUPER::list(@_) }
sub view :Chained('/customer') :PathPart('view') { shift->SUPER::view(@_) }
sub add :Chained('/customer') :PathPart('add') { shift->SUPER::add(@_) }
sub edit :Chained('/customer') :PathPart('edit') { shift->SUPER::edit(@_) }
sub delete :Chained('/customer') :PathPart('delete') { shift->SUPER::delete(@_) }
sub search :Chained('/customer') :PathPart('search') { shift->SUPER::search(@_) }
sub model :Chained('/customer') :PathPart('model') { shift->SUPER::model(@_) }
I had a red-hot go at dynamically generating all those subs via *{$action} = eval "sub ..."
and related ideas, but eventually had to admit defeat on that.
精彩评论