Mojolicious-Plugin-Vparam

 view release on metacpan or  search on metacpan

README.pod  view on Meta::CPAN


=head1 NAME

Mojolicious::Plugin::Vparam - Mojolicious plugin validator for GET/POST data.

=head1 DESCRIPTION

Features:

=over

=item *

Simple syntax or full featured

=item *

Many predefined types

=item *

Shortcuts for the most common uses

=item *

Filters complementary types

=item *

Support arrays and hashes of values

=item *

Support HTML checkbox as bool

=item *

Simple JSON values extraction and validation using JSON Pointer from
L<Mojo::JSON::Pointer>.

=item *

Simple XML/HTML values extraction and validation using CSS selector engine
from L<Mojo::DOM::CSS> or XPath from L<XML::LibXML>.

=item *

Validate all parameters at once and get hash to simple use in any Model

=item *

Manage validation errors

=item *

Full Mojolicious::Validator::Validation integration

=back

This module use simple parameters types B<str>, B<int>, B<email>, B<bool>,
etc. to validate.
Instead of many other modules you mostly not need add specific validation
subs or rules.
Just set parameter type. But if you want sub or regexp you can do it too.

=head1 SYNOPSIS

    # Add plugin in startup
    $self->plugin('Vparam');

    # Use in controller
    $login      = $self->vparam(login    => 'str');
    $passw      = $self->vparam(password => 'password', size     => [8, 100]);
    $email      = $self->vparam(email    => 'email',    optional => 1);
    $session    = $self->vparam(session  => 'bool',     default  => 1);

    $ids        = $self->vparam(ids => '@int');

=head1 METHODS

=head2 vparam

Get one parameter. By default parameter is required.

    # Simple get one parameter
    $param1 = $self->vparam(date => 'datetime');

    # Or more syntax
    $param2 = $self->vparam(count => {type => 'int', default => 1});
    # Or more simple syntax
    $param2 = $self->vparam(count => 'int', default => 1);

=head2 vparams

Get many parameters as hash. By default all parameters are required.

    %params = $self->vparams(
        # Simple syntax
        name        => 'str',
        password    => qr{^\w{8,32}$},
        myparam     => sub { $_[1] && $_[1] eq 'ok' ? 1 : 0 } },
        someone     => ['one', 'two', 'tree'],

        # More syntax
        from        => { type   => 'date', default => '' },
        to          => { type   => 'date', default => '' },
        id          => { type   => 'int' },
        money       => { regexp => qr{^\d+(?:\.\d{2})?$} },
        myparam     => { post   => sub { $_[1] && $_[1] eq 'ok' ? 1 : 0 } },

        # Checkbox
        isa         => { type => 'bool', default => 0 },
    );

=head2 vsort

Like I<vparams> but add some keys to simple use with tables. Example:

    # HTML - table with controls and filters
    Order by:
    <select name="oby">

README.pod  view on Meta::CPAN

column is primary key.

You can set different name by I<vsort_oby> config parameter.
If you set undef then parameter is not apply.

Value of B<oby> The value will be automatically mapped to the column name
using the L</-sort> attribute.
Also, the value will be checked for proper mapping.
So you do not need to worry about it.

=item ods

Sort order ASC|DESC. Default: ASC.

You can set different name by I<vsort_ods> config parameter.
If you set undef then parameter is not apply.

=back

=head2 verror $name

Get parameter error string. Return 0 if no error.

    # Get error
    print $self->verror('myparam') || 'Ok';
    # Set error
    $self->verror('myparam', {message => 'Error message'})

=head2 verrors

Return erorrs count in scalar context. In list context return erorrs hash.

    # List context get hash
    my %errors = $self->verrors;

    # Scalar context get count
    die 'Errors!' if $self->verrors;

=head2 vclass $name, @classes

Get classes for invalid input. Return empty string if no error.

    # Form example
    <input name="myparam" class="<%= vclass 'myparam' %>">
    # Return next code for invalid I<myparam>:
    # <input name="myparam" class="field-with-error">

You can set additional I<@classes> to set if field invalid.

=head2 vvalue $name, $default

Get raw input value after validation. Return I<$default> value or empty
string before validation.

    # Form example:
    <input name="myparam" value="<%= vvalue 'myparam' %>">

    # Return next code if user just open form without submit and validation:
    # <input name="myparam" value="">

    # Then user submit form and you validate id. For example user submit "abc":
    # <input name="myparam" value="abc">

=head2 vtype $name, %opts

Set new type $name if defined %opts. Else return type $name definition.

    # Get type
    $self->vtype('mytype');

    # Set type
    # load  - requires modules
    # pre   - get int
    # valid - check for not empty
    # post  - force number
    $self->vtype('mytype',
        pre     => sub {
            my ($self, $param) = @_;
            return int $param // '';
        },
        valid   => sub {
            my ($self, $param) = @_;
            return length $param ? 0 : 'Invalid'
        },
        post    => sub {
            my ($self, $param) = @_;
            return 0 + $param;
        }
    );

=head2 vfilter $name, &sub

Set new filter $name if defined %opts. Else return filter $name definition.

    # Get filter
    $self->vfilter('myfilter');

    # Set filter
    $self->vfilter('myfilter', sub {
        my ($self, $param, $expression) = @_;
        return $param eq $expression ? 0 : 'Invalid';
    });

Filter sub must return 0 if parameter value is valid. Or error string if not.

=head1 SIMPLE SYNTAX

You can use the simplified syntax instead of specifying the type,
simply by using an expression instead.

=over

=item I<REGEXP>

Apply as L</regexp> filter. No type verification, just match.

    $self->vparam(myparam => qr{^(abc|cde)$});

=item I<CODE> $mojo, $value

Apply as L</post> function. You need manual verify and set error.

README.pod  view on Meta::CPAN

    # Simple flag
    $param1 = $self->vparam(
        param1      => 'int', skip => $self->app->mode eq 'production',
    );

    # Same as by use sub.
    $param1 = $self->vparam(
        param1      => 'int', skip => sub { $_[0]->app->mode eq 'production' },
    );

If you use sub then first parameter is controller.

=head2 skipundef

By default all parameters are in output hash. You can skip parameter in result
if it`s undefined by set I<skipundef>.

    # Simple vparam
    # myparam is undef.
    $param1 = $self->vparam(param1 => 'int', optional => 1, skipundef => 1);

    # Simple flag
    # The %params hash is empty if myparam value is not integer.
    %params = $self->vparams(
        myparam     => { type => 'int', optional => 1, skipundef => 1 },
    );

    # Set all in vparams
    # The %params hash is empty if all parameters are not valid.
    %params = $self->vparams(
        -skipundef  => 1,
        param1      => 'int',
        param2      => 'str',
    );

    # Shortcut syntax: skipundef and optional is on
    $param2 = $self->vparam(param2 => '~int');

Arrays always return as arrayref. But undefined values will be skipped.

=head2 multiline

You can simple split I<textarea> to values:

    # This vparam return [1,2,3] for input "1\n2\n3\n"
    $param1 = $self->vparam(param1 => 'int', multiline => 1);

Empty lines ignored.

=head2 blessed

Keep and return blessed object for parsed parameters if available.
Vparam always return scalars if disabled.

Note: if defined I<date>, I<time>, I<datetime> then always return
formatted scalar.

=head2 jpath

If you POST data not form but raw JSON you can use JSON Pointer selectors
from L<Mojo::JSON::Pointer> to get and validate parameters.

    # POST data contains:
    # {"point":{"address":"some", "lon": 45.123456, "lat": 38.23452}}

    %opts = $self->vparams(
        address => { type => 'str', jpath => '/point/address' },
        lon     => { type => 'lon', jpath => '/point/lon' },
        lat     => { type => 'lat', jpath => '/point/lat' },
    );

Note: we don`t support multikey in json. Use hash or die.

=head2 cpath

Same as jpath but parse XML/HTML using CSS selectors from L<Mojo::DOM::CSS>.

    # POST data contains:
    # <Point>
    #    <Address>some</Address>
    #    <Lon>45.123456</Lon>
    #    <Lat>38.23452</Lat>
    # </Point>

    %opts = $self->vparams(
        address => { type => 'str', cpath => 'Point > Address' },
        lon     => { type => 'lon', cpath => 'Point > Lon' },
        lat     => { type => 'lat', cpath => 'Point > Lat' },
    );


=head2 xpath

Same as cpath but parse XML/HTML using XPath selectors from L<XML::LibXML>.

    # POST data contains:
    # <Point time="2016-11-25 14:39:00 +0300">
    #    <Address>some</Address>
    #    <Lon>45.123456</Lon>
    #    <Lat>38.23452</Lat>
    # </Point>

    %opts = $self->vparams(
        address => { type => 'str',         xpath => '/Point/Address' },
        lon     => { type => 'lon',         xpath => '/Point/Lon' },
        lat     => { type => 'lat',         xpath => '/Point/Lat' },
        time    => { type => 'datetime',    xpath => '/Point/@time' },
    );

=head1 RESERVED ATTRIBUTES

=head2 -sort

List of column names for I<vsort>. Usually not all columns visible for users and
you need convert column numbers in names. This also protect you SQL queries
from set too much or too low column number.

=head2 -optional

Set default I<optional> flag for all params in L</vparams> and L</vsort>.



( run in 0.492 second using v1.01-cache-2.11-cpan-437f7b0c052 )