CatalystX-RequestModel
view release on metacpan or search on metacpan
lib/Catalyst/ActionRole/RequestModel.pm view on Meta::CPAN
$self->_build_request_model_instance($controller, $ctx, $_)
} @qmodels;
return @matching_models;
}
sub _build_request_model_instance {
my ($self, $controller, $ctx, $request_model_class) = @_;
my $request_model_instance = $ctx->model($request_model_class)
|| croak "Request Model '$request_model_class' doesn't exist";
return $request_model_instance;
}
1;
=head1 NAME
Catalyst::ActionRole::RequestModel - Inflate a Request Model
=head1 SYNOPSIS
package Example::Controller::Account;
use Moose;
use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub root :Chained(/root) PathPart('account') CaptureArgs(0) { }
sub update :POST Chained('root') PathPart('') Args(0) Does(RequestModel) BodyModel(AccountRequest) {
my ($self, $c, $request_model) = @_;
## Do something with the $request_model
}
sub list :GET Chained('root') PathPart('') Args(0) Does(RequestModel) QueryModel(PagingModel) {
my ($self, $c, $paging_model) = @_;
}
__PACKAGE__->meta->make_immutable;
=head1 DESCRIPTION
Moves creating the request model into the action class execute phase. The following two actions are essentially
the same in effect:
sub update :POST Chained('root') PathPart('') Args(0) Does(RequestModel) BodyModel(AccountRequest) {
my ($self, $c, $request_model) = @_;
## Do something with the $request_model
}
sub update :POST Chained('root') PathPart('') Args(0) {
my ($self, $c) = @_;
my $request_model = $c->model('AccountRequest');
## Do something with the $request_model
}
The main reason for moving this into the action attributes line is the thought that it allows us
to declare the request model as meta data on the action and in the future we will be able to
introspect that meta data at application setup time to do things like generate an Open API specification.
Also, if you have several request models for the endpoint you can declare all of them on the
attributes line and we will match the incoming request to the best request model, or throw an exception
if none match. So if you have more than one this saves you writing that boilerplate code to chose and
to handled the no match conditions.
You might also just find the code neater and more clean reading. Downside is for people unfamiliar with
this system it might increase learning curve time.
=head1 ATTRITBUTE VALUE DEFAULTS
Although you may prefer to be explicit in defining the request model name, we infer default values for
both B<BodyModeL> and B<QueryModel> based on the action name and the controller namespace. For example,
package Example::Controller::Account;
use Moose;
use MooseX::MethodAttributes;
extends 'Catalyst::Controller';
sub root :Chained(/root) PathPart('account') CaptureArgs(0) { }
sub update :POST Chained('root') PathPart('') Args(0) Does(RequestModel) BodyModel() {
my ($self, $c, $request_model) = @_;
## Do something with the $request_model
}
sub list :GET Chained('root') PathPart('') Args(0) Does(RequestModel) QueryModel() {
my ($self, $c, $paging_model) = @_;
}
For the body model associated with the C<update> action, we will look for a model named
C<Example::Model::Account:UpdateBody> and for the query model associated with the C<list> action
we will look for a model named C<Example::Model::Account:ListQuery>. You can change the default
'postfix' for both types of models by defining the following methods in your controller class:
sub default_body_postfix { return 'Body' }
sub default_query_postfix { return 'Query' }
Or via the controller configuration:
__PACKAGE__->config(
default_body_postfix => 'Body',
default_query_postfix => 'Query',
);
You can also prepend a namespace affix to either the body or query model name by defining the following
methods in your controller class:
sub default_body_prefix_namespace { return 'MyApp::Model' }
sub default_query_prefix_namespace { return 'MyApp::Model' }
Or via the controller configuration:
__PACKAGE__->config(
default_body_prefix_namespace => 'MyApp::Model',
default_query_prefix_namespace => 'MyApp::Model',
);
By default both namespace prefixes are empty, while the postfixes are 'Body' and 'Query' respectively.
This I think sets a reasonable pattern that you can reuse to help make your code more consistent while
( run in 0.621 second using v1.01-cache-2.11-cpan-39bf76dae61 )