Catalyst-Manual
view release on metacpan or search on metacpan
lib/Catalyst/Manual/ExtendingCatalyst.pod view on Meta::CPAN
package MyApp::Base::Controller::FullClass;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
sub _parse_FullClass_attr {
my ($self, $app_class, $action_name, $value, $attrs) = @_;
return( ActionClass => $value );
}
1;
Note that the full line of arguments is only provided for completeness
sake. We could use this attribute in a subclass like any other
Catalyst attribute:
package MyApp::Controller::Foo;
use Moose;
use namespace::autoclean;
BEGIN { extends 'MyApp::Base::Controller::FullClass'; }
sub foo : Local FullClass('MyApp::Action::Bar') { ... }
1;
=head2 Controllers
Many things can happen in controllers, and it often improves
maintainability to abstract some of the code out into reusable base
classes.
You can provide usual Perl methods that will be available via your
controller object, or you can even define Catalyst actions which will
be inherited by the subclasses. Consider this controller base class:
package MyApp::Base::Controller::ModelBase;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
sub list : Chained('base') PathPart('') Args(0) {
my ($self, $c) = @_;
my $model = $c->model( $self->{model_name} );
my $condition = $self->{model_search_condition} || {};
my $attrs = $self->{model_search_attrs} || {};
$c->stash(rs => $model->search($condition, $attrs);
}
sub load : Chained('base') PathPart('') CaptureArgs(1) {
my ($self, $c, $id) = @_;
my $model = $c->model( $self->{model_name} );
$c->stash(row => $model->find($id));
}
1;
This example implements two simple actions. The C<list> action chains
to a (currently non-existent) C<base> action and puts a result-set
into the stash taking a configured C<model_name> as well as a search
condition and attributes. This action is a
L<chained|Catalyst::DispatchType::Chained> endpoint. The other action,
called C< load > is a chain midpoint that takes one argument. It takes
the value as an ID and loads the row from the configured model. Please
not that the above code is simplified for clarity. It misses error
handling, input validation, and probably other things.
The class above is not very useful on its own, but we can combine it
with some custom actions by sub-classing it:
package MyApp::Controller::Foo;
use Moose;
use namespace::autoclean;
BEGIN { extends 'MyApp::Base::Controller::ModelBase'; }
__PACKAGE__->config( model_name => 'DB::Foo',
model_search_condition=> { is_active => 1 },
model_search_attrs => { order_by => 'name' },
);
sub base : Chained PathPart('foo') CaptureArgs(0) { }
sub view : Chained('load') Args(0) {
my ($self, $c) = @_;
my $row = $c->stash->{row};
$c->res->body(join ': ', $row->name,
$row->description); }
1;
This class uses the formerly created controller as a base
class. First, we see the configurations that were used in the parent
class. Next comes the C<base> action, where everything chains off of.
Note that inherited actions act like they were declared in your
controller itself. You can therefor call them just by their name in
C<forward>s, C<detaches> and C<Chained(..)> specifications. This is an
important part of what makes this technique so useful.
The new C<view> action ties itself to the C<load> action specified in
the base class and outputs the loaded row's C<name> and C<description>
columns. The controller C<MyApp::Controller::Foo> now has these
publicly available paths:
=over
=item /foo
Will call the controller's C<base>, then the base classes C<list>
action.
=item /foo/$id/view
First, the controller's C<base> will be called, then it will C<load>
the row with the corresponding C<$id>. After that, C<view> will
display some fields out of the object.
=back
=head2 Models and Views
If the functionality you'd like to add is really a data-set that you
( run in 0.570 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )