Mojolicious-Plugin-FormFields
view release on metacpan or search on metacpan
lib/Mojolicious/Plugin/FormFields.pm view on Meta::CPAN
sub each
{
my $self = shift;
my $block = pop;
my $fields = $self->_to_fields;
return $fields unless ref($block) eq 'CODE';
local $_;
$block->() for @$fields;
return;
}
sub check
{
my $self = shift;
push @{$self->{checks}}, $self->{name} => shift;
$self;
}
sub filter
{
my $self = shift;
my $data = ref $_[0] eq 'CODE' ? shift : Validate::Tiny::filter(@_);
push @{$self->{filters}}, $self->{name} => $data;
$self;
}
# Just a single value
sub error
{
my $self = shift;
$self->{result}->{error}->{$self->{name}};
}
sub separator { $SEPARATOR; }
sub valid
{
my $self = shift;
return $self->{result}->{success} if defined $self->{result};
my $result;
my $name = $self->{name};
my $value = $self->{c}->param($name);
my $field = { $name => $value };
my $rules = {
fields => [ $name ],
checks => $self->{checks},
filters => $self->{filters}
};
# A bit of massaging For the is_equal() validation
my $eq = $self->{eq_to_field};
if($eq) {
$field->{$eq} = $self->{c}->param($eq);
push @{$rules->{fields}}, $eq;
}
$result = Validate::Tiny::validate($field, $rules);
$self->{c}->param($name, $result->{data}->{$name}) if @{$self->{filters}};
$self->{result} = $result;
$result->{success};
}
sub is_equal
{
my $self = shift;
$self->{eq_to_field} = $_[0];
push @{$self->{checks}}, $self->{name} => Validate::Tiny::is_equal(@_);
}
# Avoid AUTOLOAD call
sub DESTROY { }
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
(my $method = $AUTOLOAD) =~ s/[^':]+:://g;
if($method =~ /^is_/) {
my $check = Validate::Tiny->can($method);
die qq|Can't locate object method "$method" via package "${ \__PACKAGE__ }"| unless $check;
push @{$self->{checks}}, $self->{name} => $check->(@_);
}
else {
# TODO: What's the use case for this?
# field('name')->trim instead of field('name')->filter('trim')?
push @{$self->{filters}}, $self->{name} => Validate::Tiny::filter($method);
}
$self->{result} = undef; # reset previous validation
$self;
}
sub _to_string { shift->_lookup_value; }
sub _to_fields
{
my $self = shift;
my $value = $self->_lookup_value;
my $fields = [];
return $fields unless ref($value) eq 'ARRAY';
my $i = -1;
while(++$i < @$value) {
push @$fields, $self->{c}->fields($self->_path($i), $self->{object});
}
$fields;
}
sub _dom_id
{
my @name = @_;
s/[^\w]+/-/g for @name;
lib/Mojolicious/Plugin/FormFields.pm view on Meta::CPAN
$id = $self->param('id');
The flattened parameter can also be used
$name = $self->param('user.name');
See L<Mojolicious::Plugin::ParamExpand> for more info.
=head2 SCOPING
Fields can be scoped to a particular object/data structure via the C<< L</fields> >> helper
my $user = fields('user');
$user->text('name');
$user->hidden('id');
When using C<fields> you must supply the field's name to the HTML input and validation methods, otherwise
the calls are the same as they are with C<field>.
=head2 COLLECTIONS
You can also create fields scoped to elements in a collection
my $addresses = field('user.addresses');
for my $addr (@$addresses) {
# field('user.addresses.N.id')->hidden
$addr->hidden('id');
# field('user.addresses.N.street')->text
$addr->text('street');
# field('user.addresses.N.city')->select([qw|OAK PHL LAX|])
$addr->select('city', [qw|OAK PHL LAX|]);
}
Or, for fields that are already scoped
my $user = fields('user')
$user->hidden('id');
my $addressess = $user->fields('addresses');
for my $addr (@$addresses) {
$addr->hidden('id')
# ...
}
You can also access the underlying object and its position within a collection
via the C<object> and C<index> methods.
<% for my $addr (@$addresses) { %>
<div id="<%= dom_id($addr->object) %>">
<h3>Address #<%= $addr->index + 1 %></h3>
<%= $addr->hidden('id') %>
...
</div>
<% } %>
=head1 VALIDATING & FILTERING
Validation rules are created by calling validation and/or filter methods
on the field to be validated
# In your controller
my $self = shift;
$self->field('user.name')->is_required;
$self->field('user.name')->filter('trim');
These methods can be chained
$self->field('user.name')->is_required->filter('trim');
To perform validation on a field call its C<valid> method
$field = $self->field('user.name');
$field->is_required;
$field->valid;
$field->error;
This will only validate and return the error for the C<user.name> field. To validate all fields and retrieve all error messages call the controller's C<valid> and C<errors> methods
$self->field('user.name')->is_required;
$self->field('user.age')->is_like(qr/^\d+$/);
$self->valid;
my $errors = $self->errors;
$errors->{'user.name'}
# ...
Of course the C<error>/C<errors> and C<valid> methods can be used in your view too
<% unless(valid()) { %>
<p>Hey, fix the below errors</p>
<% } %>
<%= field('name')->text %>
<% unless(field('name')->valid) { %>
<span class="error"><%= field('name')->error %></span>
<% } %>
When creating validation rules for L</fields> you must pass the field name as the first argument
my $user = fields('user');
$user->is_required('password');
$user->is_equal(password => 'confirm_password');
$user->is_long_at_least(password => 8, 'Mais longo caipira');
=head2 AVAILABLE RULES & FILTERS
C<Mojolicious::Plugin::FormFields> uses C<Validate::Tiny>, see L<its docs|Validate::Tiny/filter> for a list.
=head2 RENAMING THE VALIDATION METHODS
In the event that the C<valid> and/or C<errors> methods clash with exiting methods/helpers
in your app you can rename them by specifying alternate names when loading the plugin
$self->plugin('FormFields', methods => { valid => 'form_valid', errors => 'form_errors' });
# ...
$self->field('user.name')->is_required;
$self->form_valid;
$self->form_errors;
Note that this I<only> changes the methods B<on the controller> and does not change the methods on the object returned by C<field>.
=head1 METHODS
=head2 field
field($name)->text
field($name, $object)->text
=head3 Arguments
C<$name>
The field's name, which can also be the path to its value in the stash. See L</CREATING FIELDS>.
C<$object>
( run in 3.702 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )