The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Catalyst::ActionRole::QueryParameter - Dispatch rules using query parameters

SYNOPSIS

    package MyApp::Controller::Foo;

    use Moose;
    use MooseX::MethodAttributes;

    extends 'Catalyst::Controller:';

    ## Add the ActionRole to all the Controller's actions.  You can also
    ## selectively add the ActionRole with the :Does action attribute or in
    ## controller configuration.  See Catalyst::Controller::ActionRole for
    ## more information.

    __PACKAGE__->config(
      action_roles => ['QueryParameter'],
    );

    ## Match an incoming request matching "http://myhost/path?page=1"
    sub paged_results : Path('foo') QueryParam('page') { ... }

    ## Match an incoming request matching "http://myhost/path"
    sub no_paging : Path('foo') QueryParam('!page') { ... }

    ## Match a request using a type constraint

    use Types::Standard 'Int';
    sub an_int :Path('foo') QueryParam('page:Int') { ... }

DESCRIPTION

Let's you require conditions on request query parameters (as you would access via $ctx->request->query_parameters) as part of your dispatch matching. This ActionRole is not intended to be used for general HTML form and parameter processing or validation, for that purpose there are many other options (such as HTML::FormHandler, Data::Manager or HTML::FormFu.) What it can be useful for is when you want to delegate work to various Actions inside your Controller based on what the incoming query parameters say.

Generally speaking, it is not great development practice to abuse query parameters this way. However I find there is a limited and controlled subset of use cases where this feature is valuable. As a result, the features of this ActionRole are also limited to simple defined or undefined checking, and basic Perl relational operators.

You can specify multiple QueryParams per Action. If you do have more than one we will try to match Actions that match ALL the given QueryParam attributes.

There's a functioning Catalyst example application in the test directory for your review as well.

QUERY PARAMETER CONDITION MATCHING

The value of the QueryParam attribute allows for condition matching based on query parameter definedness and via Perl relational operators. For example, you can match for a particular value or if a given value is greater than another. This can be useful when you want to perform a different Action when (for example) your user is on page 10 of a search, which might indicate they are not finding what they want and could use some additional help. I also sometimes find that I want special handling of the first page of a search result.

Although you can handle this with conditional logic inside your Action, I find the ability to declare what I want from an Action to be one of the more valuable aspects of Catalyst.

Here are some example QueryParam attributes and the queries they match:

    QueryParam('page')  ## 'page' must exist
    QueryParam('!page')  ## 'page' must NOT exist
    QueryParam('page:==1')  ## 'page' must equal numeric one
    QueryParam('page:>1')  ## 'page' must be great than one
    QueryParam('!page:>1')  ## 'page' must NOT be great than one
    QueryParam(page:Int) ## 'page' matches an Int constraint (see below)

Since as I mentioned, it is generally not awesome web development practice to make excessive use of query parameters for mapping your action logic, I have limited the condition matching to basic Perl operators. The general pattern is as follows:

    (!?)($parameter):?($condition?)

Which can be roughly translated as "A $parameter should match the $condition but we can tack a "!" to the front of the expression to reverse the match. If you don't specify a $condition, the default condition is definedness."

A $condition is basically a Perl relational operator followed by a value. Relation Operators we current support: ==,eq,>,<,!=,<=,>=,gt,ge,lt,le. In addition, we support the regular expression match operator =~. For documentation on Perl Relational Operators see: perldoc perlop. For documentation on Perl Regular Expressions see perldoc perlre.

A $condition may also be a Moose::Types or similar type constraint. See below for more.

NOTE For numeric comparisions we first check that the value 'looks_like_number' via Scalar::Util before doing the comparison. If it doesn't look like a number that is automatic fail.

USING TYPE CONSTRAINTS

To provide more flexibility and reuse in your parameter constraints, you may use types constraints as your constraint condition if you are using a recent build of Catalyst (at least version 5.90090 or greater). This allows you to use an imported type constraint, such as you might get from MooseX::Types or from Type::Tiny or Types::Standard. For example:

    package MyApp::Controller::Root;

    use base 'Catalyst::Controller';
    use Types::Standard 'Int';

    sub root :Chained(/) PathPart('') CaptureArgs(0) { }

      sub int :Chained(root) Args(0) QueryParam(page:Int) {
        my ($self, $c) = @_;
        $c->res->body('order');
      }

    MyApp::Controller::Root->config(
      action_roles => ['QueryParameter'],
    );

This would require a URL with a 'page' query that is an Integer, for example, "https://localhost/int/100".

This feature uses the type constraint resolution features built into the new versions of Catalyst so it behaves the same way.

USING CATALYST CONFIGURATION INSTEAD OF ATTRIBUTES

You may prefer to set your Query Parameter requirements via the Catalyst general application configuration, rather than in subroutine attributes. Doing so allows you to use different settings in different environments and it also allows you to use more extended values. Here's an example comparing both approaches

    ## subroutine attribute approach
    sub first_page : Path('foo') QueryParam('page:==1') { ... }

    ## configuration approach
    __PACKAGE__->config(
      action => {
        first_page => { Path => 'foo', QueryParam => 'page:==1'},
      },
    );

Since the configuration approach allows richer use of Perl, you can replace the string version of the QueryParam value with the following:

    ## configuration approach, richer Perl data structure
    __PACKAGE__->config(
      action => {
        first_page => { Path => 'foo', QueryParam => [['page','==','1']] },
        no_page_query => { Path => 'foo', QueryParam => [['!','page']] },
      },
    );

If you are using the configuration approach, this second option is preferred. Please note that since each attribute or configuration key can have an array of values, if you use the 'rich Perl data structure' approach in your configuration you will need to place the arrayref inside an arrayref as in the example above (that is not a typo!)

NOTE REGARDING CATALYST DISPATCH RESOLUTION

This document has been superceded by a new core documentation document. Please see Catalyst::RouteMatching.

LIMITATIONS

Currently this only works for 'single' query parameters. For example:

    ?foo=1&bar=2

Not:

    ?foo=1&foo=2

Patches welcomed!

AUTHOR

John Napiorkowski email:jjnapiork@cpan.org

SEE ALSO

Catalyst, Catalyst::Controller::ActionRole, Moose.

COPYRIGHT & LICENSE

Copyright 2015, John Napiorkowski email:jjnapiork@cpan.org

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.