File Coverage

blib/lib/HTML/FormFu/Constraint/DBIC/Unique.pm
Criterion Covered Total %
statement 50 65 76.9
branch 19 38 50.0
condition 11 18 61.1
subroutine 6 6 100.0
pod 0 1 0.0
total 86 128 67.1


line stmt bran cond sub pod time code
1             package HTML::FormFu::Constraint::DBIC::Unique;
2              
3 2     2   3268550 use strict;
  2         7  
  2         83  
4             our $VERSION = '2.02'; # VERSION
5              
6 2     2   12 use Moose;
  2         4  
  2         14  
7 2     2   11899 use MooseX::Attribute::FormFuChained;
  2         5  
  2         70  
8              
9             extends 'HTML::FormFu::Constraint';
10              
11 2     2   11 use Carp qw( carp croak );
  2         4  
  2         125  
12              
13 2     2   14 use HTML::FormFu::Util qw( DEBUG_CONSTRAINTS debug );
  2         4  
  2         1266  
14              
15             has model => ( is => 'rw', traits => ['FormFuChained'] );
16             has resultset => ( is => 'rw', traits => ['FormFuChained'] );
17             has column => ( is => 'rw', traits => ['FormFuChained'] );
18             has method_name => ( is => 'rw', traits => ['FormFuChained'] );
19             has self_stash_key => ( is => 'rw', traits => ['FormFuChained'] );
20             has others => ( is => 'rw', traits => ['FormFuChained'] );
21             has id_field => ( is => 'rw', traits => ['FormFuChained'] );
22              
23             sub constrain_value {
24 16     16 0 641823 my ( $self, $value ) = @_;
25              
26 16 50 33     121 return 1 if !defined $value || $value eq '';
27              
28 16         49 for (qw/ resultset /) {
29 16 50       483 if ( !defined $self->$_ ) {
30             # warn and die, as errors are swallowed by HTML-FormFu
31 0         0 carp "'$_' is not defined";
32 0         0 croak "'$_' is not defined";
33             }
34             }
35              
36             # get stash
37 16         78 my $stash = $self->form->stash;
38              
39 16         289 my $schema;
40              
41 16 50 0     52 if ( defined $stash->{schema} ) {
    0          
    0          
42 16         38 $schema = $stash->{schema};
43             }
44             elsif ( defined $stash->{context} && defined $self->model ) {
45 0         0 $schema = $stash->{context}->model( $self->model );
46             }
47             elsif ( defined $stash->{context} ) {
48 0         0 $schema = $stash->{context}->model;
49             }
50              
51 16 50       43 if ( !defined $schema ) {
52             # warn and die, as errors are swallowed by HTML-FormFu
53 0         0 carp 'could not find DBIC schema';
54 0         0 croak 'could not find DBIC schema';
55             }
56              
57 16         454 my $resultset = $schema->resultset( $self->resultset );
58              
59 16 50       6118 if ( !defined $resultset ) {
60             # warn and die, as errors are swallowed by HTML-FormFu
61 0         0 carp 'could not find DBIC resultset';
62 0         0 croak 'could not find DBIC resultset';
63             }
64              
65 16 100       519 if ( my $method_name = $self->method_name ) {
66             # warn "using $method_name to look for $value";
67              
68             # need to be able to tell $method_name about record on the form stash
69 3         6 my $pk_val;
70              
71 3 50       86 if ( defined( my $self_stash_key = $self->self_stash_key ) ) {
72              
73 3 50       14 if ( defined( my $self_stash = $stash->{ $self_stash_key } ) ) {
74              
75 3         20 my ($pk) = $resultset->result_source->primary_columns;
76              
77 3         79 $pk_val = $self_stash->$pk;
78             }
79             }
80              
81 3         124 return $resultset->$method_name( $value, $pk_val );
82             }
83             else {
84              
85 13   66     376 my $column = $self->column || $self->parent->name;
86 13         176 my %others;
87 13 100       354 if ( $self->others ) {
88 2 50       54 my @others = ref $self->others ? @{ $self->others }
  0         0  
89             : $self->others;
90              
91 2         9 my $params = $self->form->input;
92              
93             %others =
94             grep {
95             defined && length
96 4 50       58 }
97             map {
98 2         69 $_ => $self->get_nested_hash_value( $params, $_ )
  2         8  
99             } @others;
100              
101             }
102              
103 13         32 my $existing_row = eval {
104 13         82 $resultset->find( { %others, $column => $value } );
105             };
106              
107 13 50       51374 if ( my $error = $@ ) {
108             # warn and die, as errors are swallowed by HTML-FormFu
109 0         0 carp $error;
110 0         0 croak $error;
111             }
112              
113             # if a row exists, first check whether it matches a known object on the
114             # form stash
115              
116 13 50 66     436 if ( $existing_row && defined( my $self_stash_key = $self->self_stash_key ) ) {
    100 100        
117              
118 0 0       0 if ( defined( my $self_stash = $stash->{ $self_stash_key } ) ) {
119              
120 0         0 my ($pk) = $resultset->result_source->primary_columns;
121              
122 0 0       0 if ( $existing_row->$pk eq $self_stash->$pk ) {
123 0         0 return 1;
124             }
125             }
126             }
127             elsif ( $existing_row && defined (my $id_field = $self->id_field ) ) {
128 6         27 my $value = $self->get_nested_hash_value( $self->form->input, $id_field );
129 6 100 100     425 if ( defined $value && length $value ) {
130 4         29 my ($pk) = $resultset->result_source->primary_columns;
131 4         108 return ($existing_row->$pk eq $value);
132             }
133             }
134              
135 9         38 return !$existing_row;
136              
137             }
138             }
139              
140             after repeatable_repeat => sub {
141             my ( $self, $repeatable, $new_block ) = @_;
142              
143             # rename any 'id_field' fields
144             if ( my $id_field = $self->id_field ) {
145             my $block_fields = $new_block->get_fields;
146              
147             my $field = $repeatable->get_field_with_original_name( $id_field, $block_fields );
148              
149             if ( defined $field ) {
150             DEBUG_CONSTRAINTS && debug(
151             sprintf "Repeatable renaming constraint 'id_field' '%s' to '%s'",
152             $id_field,
153             $field->nested_name,
154             );
155              
156             $self->id_field( $field->nested_name );
157             }
158             }
159             };
160              
161             1;
162              
163             __END__
164              
165             =head1 NAME
166              
167             HTML::FormFu::Constraint::DBIC::Unique - unique constraint for HTML::FormFu::Model::DBIC
168              
169             =head1 VERSION
170              
171             version 2.02
172              
173             =head1 SYNOPSIS
174              
175             $form->stash->{schema} = $dbic_schema; # DBIC schema
176              
177             $form->element('text')
178             ->name('email')
179             ->constraint('DBIC::Unique')
180             ->resultset('User')
181             ;
182              
183              
184             $form->stash->{context} = $c; # Catalyst context
185              
186             $form->element('text')
187             ->name('email')
188             ->constraint('DBIC::Unique')
189             ->model('DBIC::User')
190             ;
191              
192             $form->element('text')
193             ->name('user')
194             ->constraint('DBIC::Unique')
195             ->model('DBIC')
196             ->resultset('User')
197             ;
198              
199              
200             or in a config file:
201             ---
202             elements:
203             - type: text
204             name: email
205             constraints:
206             - Required
207             - type: DBIC::Unique
208             model: DBIC::User
209             - type: text
210             name: user
211             constraints:
212             - Required
213             - type: DBIC::Unique
214             model: DBIC::User
215             column: username
216              
217              
218             =head1 DESCRIPTION
219              
220             Checks if the input value exists in a DBIC ResultSet.
221              
222             =head1 METHODS
223              
224             =head2 model
225              
226             Arguments: $string # a Catalyst model name like 'DBIC::User'
227              
228             =head2 resultset
229              
230             Arguments: $string # a DBIC resultset name like 'User'
231              
232             =head2 self_stash_key
233              
234             reference to a key in the form stash. if this key exists, the constraint
235             will check if the id matches the one of this element, so that you can
236             use your own name.
237              
238             =head2 id_field
239              
240             Use this key to define reference field which consist of primary key of
241             resultset. If the field exists (and $self_stash_key not defined), the
242             constraint will check if the id matches the primary key of row object:
243              
244             ---
245             elements:
246             - type: Hidden
247             name: id
248             constraints:
249             - Required
250              
251             - type: Text
252             name: value
253             label: Value
254             constraints:
255             - Required
256             - type: DBIC::Unique
257             resultset: ControlledVocab
258             id_field: id
259              
260             =head2 others
261              
262             Use this key to manage unique compound database keys which consist of
263             more than one column. For example, if a database key consists of
264             'category' and 'value', use a config file such as this:
265              
266             ---
267             elements:
268             - type: Text
269             name: category
270             label: Category
271             constraints:
272             - Required
273              
274             - type: Text
275             name: value
276             label: Value
277             constraints:
278             - Required
279             - type: DBIC::Unique
280             resultset: ControlledVocab
281             others: category
282              
283             =head2 method_name
284              
285             Name of a method which will be called on the resultset. The method is passed
286             two argument; the value of the field, and the primary key value (usually `id`)
287             of the record in the form stash (as defined by self_stash_key). An example
288             config might be:
289              
290             ---
291             elements:
292             - type: text
293             name: user
294             constraints:
295             - Required
296             - type: DBIC::Unique
297             model: DBIC::User
298             method_name: is_username_available
299              
300              
301             =head2 SEE ALSO
302              
303             Is a sub-class of, and inherits methods from L<HTML::FormFu::Constraint>
304              
305             L<HTML::FormFu::FormFu>
306              
307             =head1 AUTHOR
308              
309             Jonas Alves C<jgda@cpan.org>
310              
311             =head1 LICENSE
312              
313             This library is free software, you can redistribute it and/or modify it under
314             the same terms as Perl itself.