File Coverage

blib/lib/Class/DBI/FormBuilder.pm
Criterion Covered Total %
statement 35 566 6.1
branch 2 240 0.8
condition 0 113 0.0
subroutine 10 64 15.6
pod 19 19 100.0
total 66 1002 6.5


line stmt bran cond sub pod time code
1             package Class::DBI::FormBuilder;
2              
3 26     26   14459 use warnings;
  26         31  
  26         715  
4 26     26   83 use strict;
  26         29  
  26         349  
5 26     26   80 use Carp();
  26         31  
  26         243  
6              
7 26     26   75 use List::Util();
  26         25  
  26         386  
8 26     26   13215 use CGI::FormBuilder 3;
  26         428959  
  26         913  
9              
10 26     26   11867 use UNIVERSAL::require;
  26         24651  
  26         222  
11              
12             # C::FB sometimes gets confused when passed CDBI::Column objects as field names,
13             # hence all the map {''.$_} column filters. Some of them are probably unnecessary,
14             # but I need to track down which.
15              
16             our $VERSION = '0.34_1';
17              
18             our @BASIC_FORM_MODIFIERS = qw( hidden options file );
19              
20             our %ValidMap = ( varchar => 'VALUE',
21             char => 'VALUE', # includes MySQL enum and set
22             blob => 'VALUE', # includes MySQL text
23             text => 'VALUE',
24            
25             integer => 'INT',
26             bigint => 'INT',
27             smallint => 'INT',
28             tinyint => 'INT',
29            
30             date => 'VALUE',
31             time => 'VALUE',
32            
33             # normally you want to skip validating a timestamp column...
34             #timestamp => 'VALUE',
35            
36             double => 'NUM',
37             float => 'NUM',
38             decimal => 'NUM',
39             numeric => 'NUM',
40             );
41            
42             sub import
43             {
44 26     26   61 my ( $class, %args ) = @_;
45            
46 26         118 my $caller = caller(0);
47            
48 26 50       1167 $caller->can( 'form_builder_defaults' ) || $caller->mk_classdata( 'form_builder_defaults', {} );
49            
50 26         613 my @export = qw( as_form
51             search_form
52            
53             as_form_with_related
54            
55             update_or_create_from_form
56            
57             update_from_form_with_related
58            
59             retrieve_from_form
60             search_from_form
61             search_like_from_form
62             search_where_from_form
63            
64             find_or_create_from_form
65             retrieve_or_create_from_form
66             );
67            
68 26 50       96 if ( $args{BePoliteToFromForm} )
69             {
70 26     26   3409 no strict 'refs';
  26         39  
  26         2205  
71 0         0 *{"$caller\::${_}_fb"} = \&{"${_}_form"} for qw( update_from create_from );
  0         0  
  0         0  
72             }
73             else
74             {
75 26         56 push @export, qw( update_from_form create_from_form );
76             }
77            
78 26     26   102 no strict 'refs';
  26         26  
  26         77343  
79 26         83 *{"$caller\::$_"} = \&$_ for @export;
  338         2040  
80             }
81              
82             =head1 NAME
83              
84             Class::DBI::FormBuilder - Class::DBI/CGI::FormBuilder integration
85              
86             =head1 SYNOPSIS
87              
88              
89             package Film;
90             use strict;
91             use warnings;
92            
93             use base 'Class::DBI';
94             use Class::DBI::FormBuilder;
95            
96             # for automatic validation setup
97             use Class::DBI::Plugin::Type;
98            
99             # POST all forms to server
100             Film->form_builder_defaults( { method => 'post' } );
101            
102             # These fields must always be submitted for create/update routines
103             Film->columns( Required => qw( foo bar ) );
104            
105             # same thing, differently
106             # Film->form_builder_defaults->{required} = [ qw( foo bar ) ];
107            
108            
109             # In a nearby piece of code...
110            
111             my $film = Film->retrieve( $id );
112             print $film->as_form( params => $q )->render; # or $r if mod_perl
113            
114             # For a search app:
115             my $search_form = Film->search_form; # as_form plus a few tweaks
116            
117            
118             # A fairly complete app:
119            
120             my $form = Film->as_form( params => $q ); # or $r if mod_perl
121            
122             if ( $form->submitted and $form->validate )
123             {
124             # whatever you need:
125            
126             my $obj = Film->create_from_form( $form );
127             my $obj = Film->update_from_form( $form );
128             my $obj = Film->update_or_create_from_form( $form );
129             my $obj = Film->retrieve_from_form( $form );
130            
131             my $iter = Film->search_from_form( $form );
132             my $iter = Film->search_like_from_form( $form );
133             my $iter = Film->search_where_from_form( $form );
134            
135             my $obj = Film->find_or_create_from_form( $form );
136             my $obj = Film->retrieve_or_create_from_form( $form );
137            
138             print $form->confirm;
139             }
140             else
141             {
142             print $form->render;
143             }
144            
145             # See CGI::FormBuilder docs and website for lots more information.
146            
147             =head1 DESCRIPTION
148              
149             This module creates a L form from a CDBI class or object. If
150             from an object, it populates the form fields with the object's values.
151              
152             Column metadata and CDBI relationships are analyzed and the fields of the form are modified accordingly.
153             For instance, MySQL C and C columns are configured as C
154             C widgets as appropriate, and appropriate widgets are built for C, C
155             and C relationships. Further relationships can be added by subclassing.
156              
157             A demonstration app (using L) can be viewed at
158              
159             http://beerfb.riverside-cms.co.uk
160              
161             =head1 METHODS
162              
163             All the methods described here are exported into the caller's namespace, except for the form modifiers
164             (see below).
165              
166             =head2 Form generating methods
167              
168             =over 4
169              
170             =item form_builder_defaults( %args )
171              
172             Stores default arguments for the call to C.
173              
174             =item as_form( %args )
175              
176             Builds a L form representing the class or object.
177              
178             Takes default arguments from C.
179              
180             The optional hash of arguments is the same as for C, and will
181             override any keys in C.
182              
183             Note that parameter merging is likely to become more sophisticated in future releases
184             (probably copying the argument merging code from L
185             itself).
186              
187             =cut
188              
189             sub as_form
190             {
191 0     0 1   my ( $proto, %args_in ) = @_;
192            
193 0           my ( $orig, %args ) = __PACKAGE__->_get_args( $proto, %args_in );
194            
195 0           warn "as_form args_in: " . Dumper( \%args_in );
196            
197 0           __PACKAGE__->_setup_auto_validation( $proto, \%args );
198            
199 0           return __PACKAGE__->_make_form( $proto, $orig, %args );
200             }
201              
202             =begin notes
203              
204             It's impossible to know whether pk data are expected in the submitted data or not. For instance,
205             while processing a form submission:
206            
207             my $form = My::Class->as_form;
208            
209             my $obj = My::Class->retrieve_from_form( $form ); # needs pk data
210             my $obj = My::Class->find_or_create_from_form( $form ); # does not
211            
212             pk hidden fields are always present in rendered forms, but may be empty (submits undef). undef does not
213             pass validation tests. The solution is to place pk fields in 'keepextras', not in 'fields'. That means they
214             are not validated at all. The only (I think) place submitted pk data are used is in retrieve_from_form
215              
216             =end notes
217              
218             =cut
219              
220             sub _get_args
221             {
222 0     0     my ( $me, $proto, %args_in ) = @_;
223            
224 0           my %args = ( %{ $proto->form_builder_defaults }, %args_in );
  0            
225            
226             # take a copy, and make sure not to transform undef into []
227 0 0         my $original_fields = $args{fields} ? [ @{ $args{fields} } ] : undef;
  0            
228            
229 0           my %pk = map { ''.$_ => 1 } $proto->primary_columns;
  0            
230            
231 0           $args{fields} ||= [ map {''.$_}
232 0   0       grep { ! $pk{ ''.$_ } }
  0            
233             #$proto->columns( 'All' )
234             $me->_db_order_columns( $proto, 'All' )
235             ];
236            
237 0           $args{keepextras} = [ keys %pk ];
238            
239             # for objects, populate with data
240             # nb. don't say $proto->get( $_ ) because $_ may be an accessor installed by a relationship
241             # (e.g. has_many) - get() only works with real columns.
242 0 0         my @values = eval { map { '' . $proto->$_ } @{ $args{fields} } } if ref $proto;
  0            
  0            
  0            
243 0 0         die "Error populating values for $proto from '@{ $args{fields} }': $@" if $@;
  0            
244            
245 0   0       $args{values} ||= \@values;
246            
247 0           my @reqd = map {''.$_} $proto->columns( 'Required' );
  0            
248            
249 0 0 0       if ( @reqd && ! $args{required} )
250             {
251 0           $args{required} = \@reqd;
252             }
253            
254             # take care that anything in here is copied
255 0           my $orig = { fields => $original_fields };
256            
257 0           return $orig, %args;
258             }
259              
260             # Get deep into CDBI to extract the columns in the same order as defined in the database.
261             # In fact, this returns the columns in the order they were originally supplied to
262             # $proto->columns( All => [ col list ] ). Defaults
263             # to the order returned from the database query in CDBI::Loader, which for MySQL,
264             # is the same as the order in the database.
265             sub _db_order_columns
266             {
267 0     0     my ( $me, $them, $group ) = @_;
268            
269 0   0       $group ||= 'All';
270            
271 0           return @{ $them->__grouper->{_groups}->{ $group } };
  0            
272             }
273              
274             # deliberately ugly name to encourage something more generic in future
275             # this is similar to the same-named method in Maypole::FB
276             # see also _fields_and_has_many_accessors, which does a similar
277             # thing with forms
278             sub _has_many_accessors
279             {
280 0     0     my ( $me, $them ) = @_;
281            
282             # these might *not* be the correct accessor names
283 0 0         my @accessors = keys %{ $them->meta_info( 'has_many' ) || {} };
  0            
284            
285 0           return @accessors;
286             }
287              
288             sub _make_form
289             {
290 0     0     my ( $me, $them, $orig, %args ) = @_;
291            
292 0           my $form = CGI::FormBuilder->new( %args );
293            
294 0           $form->{__cdbi_original_args__} = $orig;
295            
296             # this assumes meta_info only holds data on relationships
297 0           foreach my $modify ( @BASIC_FORM_MODIFIERS, keys %{ $them->meta_info } )
  0            
298             {
299 0           my $form_modify = "form_$modify";
300            
301 0           $me->$form_modify( $them, $form );
302             }
303            
304 0           return $form;
305             }
306              
307             =item as_form_with_related
308              
309             Builds a form with fields from the target CDBI class/object, plus fields from the related objects.
310              
311             Accepts the same arguments as C, with these additions:
312              
313             =over 4
314              
315             =item related
316              
317             A hashref of C<< $field_name => $as_form_args_hashref >> settings. Each C<$as_form_args_hashref>
318             can take all the same settings as C. These are used for generating the fields of the class or
319             object(s) referred to by that field. For instance, you could use this to only display a subset of the
320             fields of the related class.
321              
322             =item show_related
323              
324             By default, all related fields are shown in the form. To only expand selected related fields, list
325             them in C.
326              
327             =back
328              
329             =cut
330              
331             sub as_form_with_related
332             {
333 0     0 1   my ( $proto, %args ) = @_;
334            
335 0           my $related_args = delete( $args{related} );
336 0   0       my $show_related = delete( $args{show_related} ) || [];
337            
338 0           my $parent_form = $proto->as_form( %args );
339            
340 0           foreach my $field ( __PACKAGE__->_fields_and_has_many_accessors( $proto, $parent_form, $show_related ) )
341             {
342             # object or class
343 0           my ( $related, $rel_type ) = __PACKAGE__->_related( $proto, $field );
344            
345 0 0         next unless $related;
346            
347 0 0         my @relateds = ref( $related ) eq 'ARRAY' ? @$related : ( $related );
348            
349 0           __PACKAGE__->_splice_form( $_, $parent_form, $field, $related_args->{ $field }, $rel_type ) for @relateds;
350             }
351            
352 0           return $parent_form;
353             }
354              
355             # deliberately ugly name to encourage something more generic in future
356             sub _fields_and_has_many_accessors
357             {
358 0     0     my ( $me, $them, $form, $show_related ) = @_;
359            
360 0 0         return @$show_related if @$show_related;
361            
362             # Cleaning these out appears not to fix multiple pc fields, but also seems like the
363             # right thing to do.
364 0           my %pc = map { $_ => 1 } $them->primary_columns;
  0            
365            
366 0           my @fields = grep { ! $pc{ $_ } } $form->field;
  0            
367            
368 0           my %seen = map { $_ => 1 } @fields;
  0            
369            
370 0 0         my @related = keys %{ $them->meta_info( 'has_many' ) || {} };
  0            
371            
372 0           push @fields, grep { ! $seen{ $_ } } @related;
  0            
373            
374 0           return @fields;
375             }
376            
377             # Add fields representing related class/object $them, to $parent_form, which represents
378             # the class/object as_form_with_related was called on. E.g. add brewery, style, and many pubs
379             # to a beer form.
380             sub _splice_form
381             {
382 0     0     my ( $me, $them, $parent_form, $field_name, $args, $rel_type ) = @_;
383            
384             # related pkdata are encoded in the fake field name
385 0           warn 'not sure if pk for related objects is getting added - if so, it should not';
386            
387 0           warn "need to add 'add relatives' button";
388 0 0         return unless ref $them; # for now
389            
390 0           my $related_form = $them->as_form( %$args );
391            
392 0           my $moniker = $them->moniker;
393            
394 0           my @related_fields;
395            
396 0           foreach my $related_field ( $related_form->fields )
397             {
398 0           my $related_field_name = $related_field->name;
399            
400 0           my $fake_name = $me->_false_related_field_name( $them, $related_field_name );
401            
402 0           $related_field->_form( $parent_form );
403            
404 0           $related_field->name( $fake_name );
405            
406             $related_field->label( ucfirst( $moniker ) . ': ' . $related_field_name )
407 0 0         unless $args->{labels}{ $related_field_name };
408            
409 0           $parent_form->{fieldrefs}{ $fake_name } = $related_field;
410            
411 0           push @related_fields, $related_field;
412             }
413              
414 0           my $offset = 0;
415            
416 0           foreach my $parent_field ( $parent_form->fields )
417             {
418 0           $offset++;
419 0 0         last if $parent_field->name eq $field_name;
420             }
421            
422 0           splice @{ $parent_form->{fields} }, $offset, 0, @related_fields;
  0            
423              
424             # different rel_types get treated differently e.g. is_a should probably not
425             # allow editing
426 0 0         if ( $rel_type eq 'has_a' )
    0          
427             {
428 0           $parent_form->field( name => $field_name,
429             type => 'hidden',
430             );
431             }
432             elsif ( $rel_type eq 'is_a' )
433             {
434             $parent_form->field( name => ''.$_,
435             readonly => 1,
436             )
437 0           for @related_fields;
438             }
439            
440             }
441            
442             # Return the class or object(s) associated with a field, if anything is associated.
443             sub _related
444             {
445 0     0     my ( $me, $them, $field ) = @_;
446            
447 0           my ( $related_class, $rel_type ) = $me->_related_class_and_rel_type( $them, $field );
448            
449 0 0         return unless $related_class;
450            
451 0 0         return ( $related_class, $rel_type ) unless ref( $them );
452            
453 0   0       my $related_meta = $them->meta_info( $rel_type => $field ) ||
454             die "No '$rel_type' meta for '$them', field '$field'";
455            
456 0           my $accessor = eval { $related_meta->accessor };
  0            
457 0 0         die "Can't find accessor in meta '$related_meta' for '$rel_type' field '$field' in '$them': $@" if $@;
458            
459             # multiple objects for has_many
460 0           my @related_objects = $them->$accessor;
461            
462 0 0         return ( $related_class, $rel_type ) unless @related_objects;
463 0 0         return ( $related_objects[0], $rel_type ) if @related_objects == 1;
464 0           return ( \@related_objects, $rel_type );
465             }
466              
467             sub _related_class_and_rel_type
468             {
469 0     0     my ( $me, $them, $field ) = @_;
470            
471 0           my @rel_types = keys %{ $them->meta_info };
  0            
472              
473 0     0     my $related_meta = List::Util::first { $_ } map { $them->meta_info( $_ => $field ) } @rel_types;
  0            
  0            
474            
475 0 0         return unless $related_meta;
476              
477 0           my $rel_type = $related_meta->name;
478            
479 0   0       my $mapping = $related_meta->{args}->{mapping} || [];
480            
481 0           my $related_class;
482            
483 0 0         if ( @$mapping )
484             {
485             #use Data::Dumper;
486             #my $foreign_meta = $related_meta->foreign_class->meta_info( 'has_a' );
487             #die Dumper( [ $mapping, $rel_type, $related_meta, $foreign_meta ] );
488             $related_class = $related_meta->foreign_class
489             ->meta_info( 'has_a' )
490 0           ->{ $$mapping[0] }
491             ->foreign_class;
492            
493 0           my $accessor = $related_meta->accessor;
494 0           my $map = $$mapping[0];
495             }
496             else
497             {
498 0           $related_class = $related_meta->foreign_class;
499             }
500            
501 0           return ( $related_class, $rel_type );
502             }
503              
504             # ------------------------------------------------------- encode / decode field names -----
505             sub _false_related_field_name
506             {
507 0     0     my ( $me, $them, $real_field_name ) = @_;
508            
509 0           my $class = $me->_encode_class( $them );
510 0           my $pk = $me->_encode_pk( $them );
511            
512 0           return $real_field_name . $class . $pk;
513             }
514              
515             sub _real_related_field_name
516             {
517 0     0     my ( $me, $field_name ) = @_;
518              
519             # remove any encoded class
520 0           $field_name =~ s/CDBI_.+_CDBI//;
521            
522             # remove any primary keys
523 0           $field_name =~ s/PKDATA_.+_PKDATA//;
524            
525 0           return $field_name;
526             }
527              
528             sub _encode_pk
529             {
530 0     0     my ( $me, $them ) = @_;
531            
532 0 0         return '' unless ref( $them );
533            
534 0           my @pk = map { $them->get( $_ ) } $them->primary_columns;
  0            
535            
536             die "dots in primary key values will confuse _encode_pk and _decode_pk"
537 0 0         if grep { /\./ } @pk;
  0            
538            
539 0           my $pk = sprintf 'PKDATA_%s_PKDATA', join( '.', @pk );
540              
541 0           return $pk;
542             }
543              
544             sub _decode_pk
545             {
546 0     0     my ( $me, $fake_field_name ) = @_;
547            
548 0 0         return unless $fake_field_name =~ /PKDATA_(.+)_PKDATA/;
549            
550 0           my $pv = $1;
551            
552 0           my @pv = split /\./, $pv;
553            
554 0           my $class = $me->_decode_class( $fake_field_name );
555            
556 0           my @pc = map { ''.$_ } $class->primary_columns;
  0            
557            
558 0           my %pk = map { $_ => shift( @pv ) } @pc;
  0            
559            
560 0           return %pk;
561             }
562              
563             sub _decode_class
564             {
565 0     0     my ( $me, $fake_field_name ) = @_;
566              
567 0           $fake_field_name =~ /CDBI_(.+)_CDBI/;
568            
569 0           my $class = $1;
570            
571 0 0         $class || die "no class in fake field name $fake_field_name";
572            
573 0           $class =~ s/\./::/g;
574            
575 0           return $class;
576             }
577              
578             sub _encode_class
579             {
580 0     0     my ( $me, $them ) = @_;
581            
582 0   0       my $token = ref( $them ) || $them;
583            
584 0           $token =~ s/::/./g;
585            
586 0           return "CDBI_$token\_CDBI";
587             }
588              
589             sub _retrieve_entity_from_fake_fname
590             {
591 0     0     my ( $me, $fake_field_name ) = @_;
592            
593 0           my $class = $me->_decode_class( $fake_field_name );
594            
595 0           my %pk = $me->_decode_pk( $fake_field_name );
596            
597 0 0         return $class unless %pk;
598            
599 0           my $obj = $class->retrieve( %pk );
600              
601 0           return $obj;
602             }
603              
604             # ------------------------------------------------------- end encode / decode field names -----
605              
606             =item search_form( %args )
607              
608             Build a form with inputs that can be fed to search methods (e.g. C).
609             For instance, all selects are multiple, and fields that normally would be required
610             are not.
611              
612             In many cases, you will want to design your own search form, perhaps only searching
613             on a subset of the available columns. Note that you can acheive that by specifying
614              
615             fields => [ qw( only these fields ) ]
616            
617             in the args.
618              
619             The following search options are available. They are only relevant if processing
620             via C.
621              
622             =over 4
623              
624             =item search_opt_cmp
625              
626             Allow the user to select a comparison operator by passing an arrayref:
627              
628             search_opt_cmp => [ ( '=', '!=', '<', '<=', '>', '>=',
629             'LIKE', 'NOT LIKE', 'REGEXP', 'NOT REGEXP',
630             'REGEXP BINARY', 'NOT REGEXP BINARY',
631             ) ]
632            
633              
634             Or, transparently set the search operator in a hidden field:
635              
636             search_opt_cmp => 'LIKE'
637            
638             =item search_opt_order_by
639              
640             If true, will generate a widget to select (possibly multiple) columns to order the results by,
641             with an C and C option for each column.
642              
643             If set to an arrayref, will use that to build the widget.
644              
645             # order by any columns
646             search_opt_order_by => 1
647            
648             # or just offer a few
649             search_opt_order_by => [ 'foo', 'foo DESC', 'bar' ]
650            
651             =back
652              
653             =cut
654              
655             sub search_form
656             {
657 0     0 1   my $proto = shift;
658            
659 0           my ( $orig, %args ) = __PACKAGE__->_get_args( $proto, @_ );
660            
661 0           my $form = __PACKAGE__->_make_form( $proto, $orig, %args );
662            
663             # make all selects multiple
664 0           foreach my $field ( $form->field )
665             {
666 0 0         next unless exists $form->field->{ $field }; # this looks a bit suspect
667            
668 0 0         $field->multiple( 1 ) if $field->options;
669            
670 0           $field->required( 0 );
671             }
672            
673             # ----- customise the search -----
674             # For processing a submitted form, remember that the field _must_ be added to the form
675             # so that its submitted value can be extracted in search_where_from_form()
676            
677             # ----- order_by
678             # this must come before adding any other fields, because the list of columns
679             # is taken from the form (not the CDBI class/object) so we match whatever
680             # column selection happened during form construction
681 0           my %order_by_spec = ( name => 'search_opt_order_by',
682             multiple => 1,
683             );
684            
685 0 0         if ( my $order_by = delete $args{search_opt_order_by} )
686             {
687 0           $order_by = [ map { $_, "$_ DESC" }
688 0 0         grep { $_->type ne 'hidden' }
  0            
689             $form->field
690             ]
691             unless ref( $order_by );
692            
693 0           $order_by_spec{options} = $order_by;
694             }
695              
696             # ----- comparison operator
697 0   0       my $cmp = delete( $args{search_opt_cmp} ) || '=';
698            
699 0           my %cmp_spec = ( name => 'search_opt_cmp' );
700            
701 0 0         if ( ref( $cmp ) )
702             {
703 0           $cmp_spec{options} = $cmp;
704 0           $cmp_spec{value} = $cmp->[0];
705 0           $cmp_spec{multiple} = undef;
706             }
707             else
708             {
709 0           $cmp_spec{value} = $cmp;
710 0           $cmp_spec{type} = 'hidden';
711             }
712              
713 0           $form->field( %cmp_spec );
714            
715 0           $form->field( %order_by_spec );
716            
717 0           return $form;
718             }
719              
720             =back
721              
722             =head2 Form modifiers
723              
724             These methods use CDBI's knowledge about its columns and table relationships to tweak the
725             form to better represent a CDBI object or class. They can be overridden if you have better
726             knowledge than CDBI does. For instance, C only knows how to figure out
727             select-type columns for MySQL databases.
728              
729             You can handle new relationship types by subclassing, and writing suitable C methods (e.g.
730             C. Your custom methods will be automatically called on the relevant fields.
731              
732             =over 4
733              
734             =item form_hidden
735              
736             Ensures primary column fields are included in the form (even if they were not included in the
737             C list), and hides them.
738              
739             =cut
740              
741             # these fields are not in the 'fields' list, but are in 'keepextras'
742             sub form_hidden
743             {
744 0     0 1   my ( $me, $them, $form ) = @_;
745            
746 0           foreach my $field ( map {''.$_} $them->primary_columns )
  0            
747             {
748 0 0         my $value = $them->get( $field ) if ref( $them );
749            
750 0           $form->field( name => $field,
751             type => 'hidden',
752             value => $value,
753             );
754             }
755             }
756              
757             =item form_options
758              
759             Identifies column types that should be represented as select, radiobutton or
760             checkbox widgets. Currently only works for MySQL C columns.
761              
762             There is a simple patch for L that enables this for MySQL C
763             columns - see http://rt.cpan.org/NoAuth/Bug.html?id=12971
764              
765             Patches are welcome for similar column types in other RDBMS's.
766              
767             Note that you can easily emulate a MySQL C column by setting the validation for the column
768             to an arrayref of values. Haven't poked around yet to see how easily a C column can
769             be emulated.
770              
771             =cut
772              
773             sub form_options
774             {
775 0     0 1   my ( $me, $them, $form ) = @_;
776            
777 0           foreach my $field ( map {''.$_} $them->columns('All') )
  0            
778             {
779 0 0         next unless exists $form->field->{ $field }; # $form->field( name => $field );
780            
781 0           my ( $series, $multiple ) = $me->_get_col_options_for_enumlike( $them, $field );
782            
783 0 0         next unless @$series;
784            
785 0 0         my $value = $them->get( $field ) if ref( $them );
786            
787 0           $form->field( name => $field,
788             options => $series,
789             multiple => $multiple,
790             value => $value,
791             );
792             }
793             }
794              
795             # also used in _auto_validate
796             sub _get_col_options_for_enumlike
797             {
798 0     0     my ( $me, $them, $col ) = @_;
799            
800 0           my ( @series, $multiple );
801              
802             CASE: {
803             # MySQL enum
804 0 0         last CASE if @series = eval { $them->enum_vals( $col ) };
  0            
  0            
805             # MySQL set
806 0 0         $multiple++, last CASE if @series = eval { $them->set_vals( $col ) };
  0            
807            
808             # other dbs go here
809             }
810            
811 0           return \@series, $multiple;
812             }
813              
814             =item form_file
815              
816             B - at the moment, you need to set the field type to C manually.
817              
818             Figures out if a column contains file data.
819              
820             =cut
821              
822             sub form_file
823             {
824 0     0 1   my ( $me, $them, $form ) = @_;
825              
826 0           return;
827             }
828              
829             =item form_has_a
830              
831             Populates a select-type widget with entries representing related objects. Makes the field
832             required.
833              
834             Note that this list will be very long if there are lots of rows in the related table.
835             You may need to override this method in that case. For instance, overriding with a
836             no-op will result in a standard C type input widget.
837              
838             This method assumes the primary key is a single column - patches welcome.
839              
840             Retrieves every row and creates an object for it - not good for large tables.
841              
842             If the relationship is to a non-CDBI class, loads a plugin to handle the field (see below - Plugins).
843              
844             =cut
845              
846             sub form_has_a
847             {
848 0     0 1   my ( $me, $them, $form ) = @_;
849            
850 0   0       my $meta = $them->meta_info( 'has_a' ) || return;
851            
852 0           my @haves = keys %$meta;
853            
854 0           foreach my $field ( @haves )
855             {
856             #$me->_set_field_options( $them, $form, $field, { required => 1 } ) || next;
857 0 0         next unless exists $form->field->{ $field };
858            
859 0           my ( $related_class, undef ) = $me->_related_class_and_rel_type( $them, $field );
860            
861 0 0         if ( $related_class->isa( 'Class::DBI' ) )
862             {
863 0   0       my $options = $me->_field_options( $them, $form, $field ) ||
864             die "No options detected for field '$field'";
865            
866 0           my ( $related_object, $value );
867            
868 0 0         if ( ref $them )
869             {
870 0   0       $related_object = $them->get( $field ) || die sprintf
871             'Failed to retrieve a related object from %s has_a field %s - inconsistent db?',
872             ref( $them ), $field;
873            
874 0           my $pk = $related_object->primary_column;
875            
876 0           $value = $related_object->$pk;
877             }
878            
879 0           $form->field( name => $field,
880             options => $options,
881             required => 1,
882             value => $value,
883             );
884             }
885             else
886             {
887 0           my $class = "Class::DBI::FormBuilder::Plugin::$related_class";
888            
889 0 0         if ( $class->require )
890             {
891 0           $class->field( $them, $form, $field );
892             }
893             # elsif ( $@ =~ // ) XXX
894             # {
895             # # or simply stringify
896             # $form->field( name => $field,
897             # required => 1,
898             # value => $them->$field.'',
899             # );
900             # }
901             else
902             {
903 0           die "Failed to load $class: $@";
904             }
905             }
906            
907             }
908             }
909              
910             =begin notes
911              
912             package Class::DBI::FormBuilder::Plugin::Time::Piece;
913             use strict;
914             use warnings FATAL => 'all';
915              
916             #use Class::DBI::Plugin::Type; # not needed for mysql
917              
918             # takes a list of stuff, calls/returns $form->field(%args)
919             #
920             sub field
921             {
922             my ( $class, $them, $form, $field ) = @_;
923              
924             my $type = $them->column_type( $field );
925              
926             my $value = $them->$field.''; # lousy default
927            
928             my $validate = undef;
929            
930             if ( $type eq 'time' )
931             {
932             $value = $them->$field->hms;
933            
934             $validate = '/\d\d:\d\d:\d\d/';
935             } elsif ( $type eq 'date' )
936             {
937             $value = $them->$field->ymd;
938            
939             $validate = '/\d{4}-\d\d-\d\d/';
940             } else
941             {
942             die "don't understand column type '$type'";
943             }
944            
945             $form->field( name => $field,
946             value => $value,
947             required => 1,
948             validate => $validate,
949             );
950             }
951              
952             =end notes
953              
954             =item form_has_many
955              
956             Also assumes a single primary column.
957              
958             =cut
959              
960             sub form_has_many
961             {
962 0     0 1   my ( $me, $them, $form ) = @_;
963            
964 0   0       my $meta = $them->meta_info( 'has_many' ) || return;
965            
966 0           my @extras = keys %$meta;
967            
968 0 0         my %allowed = map { $_ => 1 } @{ $form->{__cdbi_original_args__}->{fields} || [ @extras ] };
  0            
  0            
969            
970 0           my @wanted = grep { $allowed{ $_ } } @extras;
  0            
971            
972             #$form->field( name => $_, multiple => 1 ) for @wanted;
973            
974             # The target class/object ($them) does not have a column for the related class,
975             # so we need to add these to the form, then figure out their options.
976             # Need to make sure and set some attribute to create the new field.
977             # BUT - do not create the new field if it wasn't in the list passed in the original
978             # args, or if [] was passed in the original args.
979            
980 0           foreach my $field ( @wanted )
981             {
982             # the 'next' condition is not tested because @wanted lists fields that probably
983             # don't exist yet, but should
984             #next unless exists $form->field->{ $field };
985            
986 0   0       my $options = $me->_field_options( $them, $form, $field ) ||
987             die "No options detected for '$them' field '$field'";
988            
989 0           my @many_pks;
990            
991 0 0         if ( ref $them )
992             {
993 0           my $rel = $meta->{ $field };
994            
995 0   0       my $accessor = $rel->accessor || die "no accessor for $field";
996            
997 0           my ( $related_class, undef ) = $me->_related_class_and_rel_type( $them, $field );
998 0 0         die "no foreign_class for $field" unless $related_class;
999            
1000 0           my $foreign_pk = $related_class->primary_column;
1001            
1002             # don't be tempted to access pks directly in $iter->data - they may refer to an
1003             # intermediate table via a mapping method
1004 0           my $iter = $them->$accessor;
1005            
1006 0           while ( my $obj = $iter->next )
1007             {
1008 0 0         die "retrieved " . ref( $obj ) . " '$obj' is not a $related_class"
1009             unless ref( $obj ) eq $related_class;
1010            
1011 0           push @many_pks, $obj->$foreign_pk;
1012             }
1013             }
1014            
1015 0           $form->field( name => $field,
1016             value => \@many_pks,
1017             options => $options,
1018             multiple => 1,
1019             );
1020             }
1021             }
1022              
1023             =item form_might_have
1024              
1025             Also assumes a single primary column.
1026              
1027             =cut
1028              
1029             # this code is almost identical to form_has_many
1030             sub form_might_have
1031             {
1032 0     0 1   my ( $me, $them, $form ) = @_;
1033            
1034 0   0       my $meta = $them->meta_info( 'might_have' ) || return;
1035            
1036 0           my @extras = keys %$meta;
1037            
1038 0 0         my %allowed = map { $_ => 1 } @{ $form->{__cdbi_original_args__}->{fields} || [ @extras ] };
  0            
  0            
1039            
1040 0           my @wanted = grep { $allowed{ $_ } } @extras;
  0            
1041            
1042 0           foreach my $field ( @wanted )
1043             {
1044             # the 'next' condition is not tested because @wanted lists fields that probably
1045             # don't exist yet, but should
1046            
1047 0   0       my $options = $me->_field_options( $them, $form, $field ) ||
1048             die "No options detected for '$them' field '$field'";
1049              
1050 0           my $might_have_object_id;
1051            
1052 0 0         if ( ref $them )
1053             {
1054 0           my $rel = $meta->{ $field };
1055            
1056 0   0       my $accessor = $rel->accessor || die "no accessor for $field";
1057              
1058 0           my ( $related_class, undef ) = $me->_related_class_and_rel_type( $them, $field );
1059 0 0         die "no foreign_class for $field" unless $related_class;
1060            
1061 0           my $foreign_pk = $related_class->primary_column;
1062            
1063 0           my $might_have_object = $them->$accessor;
1064            
1065 0 0         if ( $might_have_object )
1066             {
1067 0 0         die "retrieved " . ref( $might_have_object ) . " '$might_have_object' is not a $related_class"
1068             unless ref( $might_have_object ) eq $related_class;
1069             }
1070            
1071 0 0         $might_have_object_id = $might_have_object ? $might_have_object->$foreign_pk : undef; # was ''
1072             }
1073            
1074 0           $form->field( name => $field,
1075             value => $might_have_object_id,
1076             options => $options,
1077             );
1078             }
1079             }
1080              
1081             sub _field_options
1082             {
1083 0     0     my ( $me, $them, $form, $field ) = @_;
1084            
1085 0           my ( $related_class, undef ) = $me->_related_class_and_rel_type( $them, $field );
1086            
1087 0 0         return unless $related_class;
1088            
1089 0 0         return unless $related_class->isa( 'Class::DBI' );
1090            
1091 0           my $iter = $related_class->retrieve_all;
1092            
1093 0           my $pk = $related_class->primary_column;
1094            
1095 0           my @options;
1096            
1097 0           while ( my $object = $iter->next )
1098             {
1099 0           push @options, [ $object->$pk, ''.$object ];
1100             }
1101            
1102 0           return \@options;
1103             }
1104              
1105             =back
1106              
1107             =head2 Form handling methods
1108              
1109             B: if you want to use this module alongside L,
1110             load the module like so
1111              
1112             use Class::DBI::FormBuilder BePoliteToFromForm => 1;
1113            
1114             and the following 2 methods will instead be imported as C and C.
1115              
1116             You might want to do this if you have more complex validation requirements than L provides.
1117              
1118             All these methods check the form like this
1119              
1120             return unless $fb->submitted && $fb->validate;
1121            
1122             which allows you to say things like
1123              
1124             print Film->update_from_form( $form ) ? $form->confirm : $form->render;
1125            
1126             That's pretty concise!
1127              
1128             =over 4
1129              
1130             =item create_from_form( $form )
1131              
1132             Creates and returns a new object.
1133              
1134             =cut
1135              
1136             sub create_from_form
1137             {
1138 0     0 1   my ( $class, $fb ) = @_;
1139            
1140 0 0         Carp::croak "create_from_form can only be called as a class method" if ref $class;
1141            
1142 0           __PACKAGE__->_run_create( $class, $fb );
1143             }
1144              
1145             sub _run_create
1146             {
1147 0     0     my ( $me, $class, $fb ) = @_;
1148            
1149 0 0 0       return unless $fb->submitted && $fb->validate;
1150            
1151 0           my $them = bless {}, $class;
1152            
1153 0           my $cols = {};
1154            
1155             # this assumes no extra fields in the form
1156             #return $class->create( $fb->fields );
1157            
1158 0           my $data = $fb->fields;
1159            
1160 0           foreach my $col ( map {''.$_} $them->columns('All') )
  0            
1161             {
1162 0           $cols->{ $col } = $data->{ $col };
1163             }
1164            
1165             #return $me->_create_object( $class, $cols );
1166 0           return $class->create( $cols );
1167             }
1168              
1169             =begin crud
1170              
1171             # If pk values are created in the database (e.g. in a MySQL AUTO_INCREMENT
1172             # column), then they will not be available in the new object. Neither will
1173             # anything else, because CDBI discards all data before returning the new
1174             # object.
1175             sub _create_object
1176             {
1177             my ( $me, $class, $data ) = @_;
1178            
1179             die "_create_object needs a CDBI class, not an object" if ref( $class );
1180            
1181             my $obj = $class->create( $data );
1182            
1183             my @pcs = map { $obj->get( $_ ) } $obj->primary_columns;
1184            
1185             my $ok;
1186             $ok &&= $_ for @pcs;
1187            
1188             return $obj if $ok; # every primary column has a value
1189            
1190             die "No pks for new object $obj" unless @pcs == 1; # 1 undef value - we can find it
1191            
1192             # this works for MySQL and SQLite - these may be the only dbs that don't
1193             # supply the pk data in the first place?
1194             my $id = $obj->_auto_increment_value;
1195            
1196             return $class->retrieve( $id ) || die "Could not retrieve newly created object with ID '$id'";
1197             }
1198              
1199             =end crud
1200              
1201             =item update_from_form( $form )
1202              
1203             Updates an existing CDBI object.
1204              
1205             If called on an object, will update that object.
1206              
1207             If called on a class, will first retrieve the relevant object (via C).
1208              
1209             =cut
1210              
1211             sub update_from_form
1212             {
1213 0     0 1   my $proto = shift;
1214            
1215 0 0         my $them = ref( $proto ) ? $proto : $proto->retrieve_from_form( @_ );
1216            
1217 0 0         Carp::croak "No object found matching submitted primary key data" unless $them;
1218            
1219 0           __PACKAGE__->_run_update( $them, @_ );
1220             }
1221              
1222             sub _run_update
1223             {
1224 0     0     my ( $me, $them, $fb ) = @_;
1225            
1226 0 0 0       return unless $fb->submitted && $fb->validate;
1227            
1228 0           my $formdata = $fb->fields;
1229            
1230             # I think this is now unnecessary (0.4), because pks are in keepextras
1231 0           delete $formdata->{ $_ } for map {''.$_} $them->primary_columns;
  0            
1232            
1233             # Start with all possible columns. Only ask for the subset represented
1234             # in the form. This allows correct handling of fields that result in
1235             # 'missing' entries in the submitted data - e.g. checkbox groups with
1236             # no item selected will not even appear in the raw request data, but here
1237             # they should result in an undef value being sent to the object.
1238             # We need to do this filtering because there can be many-many fields, which
1239             # do not represent columns and would raise an error if we tried to update
1240             # the object with them. Otherwise, we could have trusted FB to only give us
1241             # the relevant fields in $formdata and not needed to filter for columns( 'All' )
1242 0           my %coldata = map { $_ => $formdata->{ $_ } }
1243 0           grep { exists $formdata->{ $_ } }
  0            
1244             $them->columns( 'All' );
1245            
1246             # A has_many relationship means an object is linked to 0..* objects in
1247             # another table, *and* no other object is linked to them. The link is set up
1248             # at the moment the related object is created, via
1249             # $brewery->add_to_beers( { name => 'Dark Island', abv => 4.3, etc... } );
1250            
1251             # Such has_many relationships are not handled by this form, so we can ignore this.
1252            
1253             # But the beers <-> pubs relationship is many-many, with a linking table.
1254             # The map in BeerDB::Beer is 'pub' - i.e. calling $beer->pubs fetches BeerDB::HandPump
1255             # objects. $handpump->pub is called on each, and the pub objects returned.
1256             # Similarly, the map in BeerDB::Pub is 'beer': $pub->beers returns beers
1257             # via $handpump->beer
1258            
1259             # So, to add a link between a beer and a pub, the docs say we can just call
1260             # $beer->add_to_pubs( { pub => $pub } )
1261             # and it will DTRT - add a new entry in the HandPump table.
1262             # Similarly, cascading deletes will delete the handpump, not the pub/beer on
1263             # the other end of the relationship.
1264            
1265             # this data is assumed to only be primary keys - the objects already exist
1266 0           my %many_many_data = map { $_ => [ $fb->field( $_ ) ] }
1267 0           grep { exists $formdata->{ $_ } }
  0            
1268             $me->_has_many_accessors( $them );
1269            
1270 26     26   14158 use Data::Dumper;
  26         133546  
  26         56072  
1271 0           warn "Extracted data; " . Dumper( \%coldata );
1272 0           warn "Formdata: " . Dumper( $formdata );
1273 0           warn "has_many data: " . Dumper( \%many_many_data );
1274            
1275 0           $them->set( %coldata );
1276            
1277             # pubs
1278 0           foreach my $accessor ( keys %many_many_data )
1279             {
1280             # add_to_pubs
1281 0           my $add_to_accessor = "add_to_$accessor";
1282            
1283             # e.g. $them isa BeerDB::Beer, and we want to link it to a pub
1284             # $beer->meta_info( has_many => 'pubs' );
1285 0           my $meta = $them->meta_info( has_many => $accessor );
1286            
1287             # pub
1288 0           my $map = $meta->args->{mapping}->[0];
1289            
1290 0           my $foreign_class = $meta->foreign_class # BeerDB::HandPump
1291             ->meta_info( has_a => $map ) # pub
1292             ->foreign_class; # BeerDB::Pub
1293            
1294             # %pub_ids = map { $_ => 1 } $beer->pubs;
1295 0           my %current_items = map { $_->id => 1 } $them->$accessor;
  0            
1296            
1297 0           foreach my $item ( @{ $many_many_data{ $accessor } } )
  0            
1298             {
1299 0 0         next if $current_items{ $item };
1300            
1301             # $pub = BeerDB::Pub->retrieve( $x );
1302 0           my $related_object = $foreign_class->retrieve( $item );
1303            
1304             # $beer->add_to_pubs( { pub => $pub } );
1305 0           $them->$add_to_accessor( { $map => $related_object } );
1306            
1307             # eval { $them->$accessor( $has_many_data{ $accessor } ) };
1308             # die "Error calling has_many accessor '$accessor' on '$them' with data " .
1309             # "'@{ $has_many_data{ $accessor } }': $@" if $@;
1310             }
1311             }
1312            
1313 0           $them->update;
1314            
1315 0           return $them;
1316             }
1317              
1318             =item update_from_form_with_related
1319              
1320             Sorry about the name, alternative suggestions welcome.
1321              
1322             =cut
1323              
1324             sub update_from_form_with_related
1325             {
1326 0     0 1   my ( $proto, $form ) = @_;
1327            
1328 0 0         my $them = ref( $proto ) ? $proto : $proto->retrieve_from_form( $form );
1329            
1330 0 0         Carp::croak "No object found matching submitted primary key data" unless $them;
1331            
1332 0 0         Carp::croak "Still not an object: $them" unless ref( $them );
1333            
1334 0 0         die "Not a form: $form" unless $form->isa( 'CGI::FormBuilder' );
1335            
1336 0           __PACKAGE__->_run_update_from_form_with_related( $them, $form );
1337             }
1338              
1339             sub _run_update_from_form_with_related
1340             {
1341 0     0     my ( $me, $them, $fb ) = @_;
1342            
1343 0 0 0       return unless $fb->submitted && $fb->validate;
1344            
1345             # Don't think about relationships. We have form data that can be associated
1346             # with specific objects in different classes, or with the creation of new
1347             # objects in different classes. Just decode the form field names, collect
1348             # each set of data, and send to CDBI
1349            
1350 0           my $struct = $me->_extract_data_from_form_with_related( $fb );
1351            
1352             # entries are class names or PARENT, entities are class names or objects
1353             # (or no entity for PARENT)
1354 0           foreach my $entry ( keys %$struct )
1355             {
1356 0           my $formdata = $struct->{ $entry }->{data};
1357 0           my $entity = $struct->{ $entry }->{entity};
1358            
1359             # the parent object has no entity in $struct
1360 0   0       $entity ||= $them;
1361            
1362             # Start with all possible columns. Only ask for the subset represented
1363             # in the form. This allows correct handling of fields that result in
1364             # 'missing' entries in the submitted data - e.g. checkbox groups with
1365             # no item selected will not even appear in the raw request data, but here
1366             # they should result in an undef value being sent to the object.
1367 0           my %coldata = map { $_ => $formdata->{ $_ } }
1368 0           grep { exists $formdata->{ $_ } }
  0            
1369             $entity->columns( 'All' );
1370            
1371 0 0         if ( ref $entity )
1372             { # update something that already exists
1373            
1374             # XXX hack - this stuff should not be in the form, or should be in cgi_params (maybe)
1375 0           my %pk = map { $_ => 1 } $entity->primary_columns;
  0            
1376 0           my $found_pk = 0;
1377 0           $found_pk++ for grep { $pk{ $_ } } keys %coldata;
  0            
1378 0 0         warn sprintf( "Got pk data for '%s' (%s) in formdata", $entity, ref( $entity ) )
1379             if $found_pk;
1380 0           delete $coldata{ $_ } for keys %pk;
1381            
1382 0           $entity->set( %coldata );
1383            
1384 0           $entity->update;
1385             }
1386             else
1387             { # create something new
1388 0           my $class = $entity;
1389            
1390 0           $entity = $class->create( \%coldata );
1391            
1392             # just for tidiness - probably not going to need to keep the struct
1393             #$struct->{ $entity } = delete $struct->{ $class };
1394            
1395             # relate it to parent
1396 0 0         $me->_setup_relationships_between( $them, $entity ) ||
1397             die "failed to set up any relationships between parent '$them' and new object '$entity'";
1398            
1399             }
1400             }
1401            
1402 0           return $them;
1403             }
1404              
1405             sub _extract_data_from_form_with_related
1406             {
1407 0     0     my ( $me, $fb ) = @_;
1408              
1409 0           my $formdata = $fb->fields;
1410            
1411 0           my $struct;
1412            
1413 0           foreach my $field ( keys %$formdata )
1414             {
1415 0           my $real_field_name = $me->_real_related_field_name( $field );
1416            
1417 0 0         if ( $real_field_name eq $field )
1418             {
1419 0           $struct->{PARENT}{data}{ $field } = $formdata->{ $field };
1420             #$struct->{ ref $them }{entity} ||= $them;
1421             }
1422             else
1423             {
1424             # class or object
1425 0           my $related = $me->_retrieve_entity_from_fake_fname( $field );
1426            
1427 0   0       my $related_class = ref( $related ) || $related;
1428            
1429 0           $struct->{ $related_class }{data}{ $real_field_name } = $formdata->{ $field };
1430 0   0       $struct->{ $related_class }{entity} ||= $related;
1431             }
1432             }
1433            
1434 0           return $struct;
1435             }
1436              
1437             =begin previously
1438              
1439             # $them is either the parent object, or a related object or class.
1440             # Make sure the parent doesn't get transformed into a class.
1441             sub _extract_data_from_form_with_related
1442             {
1443             my ( $me, $them, $fb ) = @_;
1444              
1445             my $formdata = $fb->fields;
1446            
1447             my %pk = map { $_ => 1 } $them->primary_columns;
1448            
1449             my $struct;
1450            
1451             foreach my $field ( keys %$formdata )
1452             {
1453             my $real_field_name = $me->_real_related_field_name( $field );
1454            
1455             warn "Got pk data (field '$real_field_name' as '$field') for $them in formdata"
1456             if $pk{ $real_field_name };
1457            
1458             next if $pk{ $real_field_name };
1459            
1460             if ( $real_field_name eq $field )
1461             {
1462             $struct->{ ref $them }{data}{ $field } = $formdata->{ $field };
1463             $struct->{ ref $them }{entity} ||= $them;
1464             }
1465             else
1466             {
1467             # class or object
1468             my $related = $me->_retrieve_entity_from_fake_fname( $field );
1469            
1470             my $related_class = ref( $related ) || $related;
1471            
1472             $struct->{ $related_class }{data}{ $real_field_name } = $formdata->{ $field };
1473             $struct->{ $related_class }{entity} ||= $related;
1474             }
1475             }
1476            
1477             return $struct;
1478             }
1479              
1480             =end previously
1481              
1482             =cut
1483              
1484             # I'm nervous that I can create an object and *then* set up its relationships,
1485             # but that seems to be the easiest way to go:
1486              
1487             # create new object
1488             # inspect its meta for relationships back to the parent
1489             # if there are any, get the mutator from the meta
1490             # call the mutator with the parent as argument
1491             # then inspect the parent's meta for relationships to the new object
1492             # if there are any, get the mutator from the meta
1493             # call the mutator with the child as argument
1494             sub _setup_relationships_between
1495             {
1496 0     0     my ( $me, $them, $related ) = @_;
1497            
1498 0 0         die "root object must be an object - got $them" unless ref( $them );
1499 0 0         die "related object must be an object - got $related" unless ref( $related );
1500            
1501 0           my $made_rels = 0;
1502            
1503 0           foreach my $meta_accessor ( $me->_meta_accessors( $related ) )
1504             {
1505 0           my ( $related_class, $rel_type ) = $me->_related_class_and_rel_type( $related, $meta_accessor );
1506            
1507 0 0 0       next unless $related_class && ( ref( $them ) eq $related_class );
1508            
1509 0           $related->$meta_accessor( $them );
1510            
1511 0           $made_rels++;
1512            
1513 0           last;
1514             }
1515            
1516 0           foreach my $meta_accessor ( $me->_meta_accessors( $them ) )
1517             {
1518 0           my ( $related_class, $rel_type ) = $me->_related_class_and_rel_type( $them, $meta_accessor );
1519            
1520 0 0 0       next unless $related_class && ( ref( $related ) eq $related_class );
1521            
1522 0           $them->$meta_accessor( $related );
1523            
1524 0           $made_rels++;
1525            
1526 0           last;
1527             }
1528            
1529 0           return $made_rels;
1530             }
1531              
1532             # like columns( 'All' ), but only for things in meta - so includes has_many accessors,
1533             # which don't occur in columns( 'All' )
1534             sub _meta_accessors
1535             {
1536 0     0     my ( $me, $them ) = @_;
1537            
1538 0           my @accessors;
1539            
1540 0           foreach my $rel_type ( keys %{ $them->meta_info } )
  0            
1541             {
1542 0           push @accessors, keys %{ $them->meta_info( $rel_type ) };
  0            
1543             }
1544              
1545 0           return @accessors;
1546             }
1547              
1548             =item update_or_create_from_form
1549              
1550             Class method.
1551              
1552             Attempts to look up an object (using primary key data submitted in the form) and update it.
1553              
1554             If none exists (or if no values for primary keys are supplied), a new object is created.
1555              
1556             =cut
1557              
1558             sub update_or_create_from_form
1559             {
1560 0     0 1   my $class = shift;
1561            
1562 0 0         Carp::croak "update_or_create_from_form can only be called as a class method" if ref $class;
1563              
1564 0           __PACKAGE__->_run_update_or_create_from_form( $class, @_ );
1565             }
1566              
1567             sub _run_update_or_create_from_form
1568             {
1569 0     0     my ( $me, $them, $fb ) = @_;
1570              
1571 0 0 0       return unless $fb->submitted && $fb->validate;
1572              
1573             #my $formdata = $fb->fields;
1574              
1575 0           my $object = $them->retrieve_from_form( $fb );
1576            
1577 0 0         return $object->update_from_form( $fb ) if $object;
1578            
1579 0           $them->create_from_form( $fb );
1580             }
1581              
1582             =back
1583              
1584             =head2 Search methods
1585              
1586             Note that search methods (except for C) will return a CDBI iterator
1587             in scalar context, and a (possibly empty) list of objects in list context.
1588              
1589             All the search methods except C require that the submitted form should either be built using
1590             C (not C), or should supply all C (including C) fields.
1591             Otherwise they may fail validation checks for missing required fields.
1592              
1593             =over 4
1594              
1595             =item retrieve_from_form
1596              
1597             Use primary key data in a form to retrieve a single object.
1598              
1599             =cut
1600              
1601             sub retrieve_from_form
1602             {
1603 0     0 1   my $class = shift;
1604            
1605 0 0         Carp::croak "retrieve_from_form can only be called as a class method" if ref $class;
1606              
1607 0           __PACKAGE__->_run_retrieve_from_form( $class, @_ );
1608             }
1609              
1610             sub _run_retrieve_from_form
1611             {
1612 0     0     my ( $me, $them, $fb ) = @_;
1613            
1614             # we don't validate because pk data must side-step validation as it's
1615             # unknowable in advance whether they will even be present.
1616             #return unless $fb->submitted && $fb->validate;
1617            
1618 0   0       my %pkdata = map { $_ => $fb->cgi_param( ''.$_ ) || undef } $them->primary_columns;
  0            
1619            
1620 0           return $them->retrieve( %pkdata );
1621             }
1622              
1623             =item search_from_form
1624              
1625             Lookup by column values.
1626              
1627             =cut
1628              
1629             sub search_from_form
1630             {
1631 0     0 1   my $class = shift;
1632            
1633 0 0         Carp::croak "search_from_form can only be called as a class method" if ref $class;
1634              
1635 0           __PACKAGE__->_run_search_from_form( $class, '=', @_ );
1636             }
1637              
1638             =item search_like_from_form
1639              
1640             Allows wildcard searches (% or _).
1641              
1642             Note that the submitted form should be built using C, not C.
1643              
1644             =cut
1645              
1646             sub search_like_from_form
1647             {
1648 0     0 1   my $class = shift;
1649            
1650 0 0         Carp::croak "search_like_from_form can only be called as a class method" if ref $class;
1651              
1652 0           __PACKAGE__->_run_search_from_form( $class, 'LIKE', @_ );
1653             }
1654              
1655             sub _run_search_from_form
1656             {
1657 0     0     my ( $me, $them, $search_type, $fb ) = @_;
1658            
1659 0 0 0       return unless $fb->submitted && $fb->validate;
1660              
1661 0           my %searches = ( LIKE => 'search_like',
1662             '=' => 'search',
1663             );
1664            
1665 0           my $search_method = $searches{ $search_type };
1666            
1667 0           my @search = $me->_get_search_spec( $them, $fb );
1668            
1669             # Probably you would normally sort results in the output page, rather
1670             # than in the search form. Might be useful to specify the initial sort order
1671             # in a hidden 'sort_by' field.
1672 0           my @modifiers = qw( order_by order_direction ); # others too
1673            
1674 0           my %search_modifiers = $me->_get_search_spec( $them, $fb, \@modifiers );
1675            
1676 0 0         push( @search, \%search_modifiers ) if %search_modifiers;
1677            
1678 0           return $them->$search_method( @search );
1679             }
1680              
1681             sub _get_search_spec
1682             {
1683 0     0     my ( $me, $them, $fb, $fields ) = @_;
1684              
1685 0 0         my @fields = $fields ? @$fields : $them->columns( 'All' );
1686              
1687             # this would miss multiple items
1688             #my $formdata = $fb->fields;
1689            
1690 0           my $formdata;
1691            
1692 0           foreach my $field ( $fb->fields )
1693             {
1694 0           my @data = $field->value;
1695            
1696 0 0         $formdata->{ $field } = @data > 1 ? \@data : $data[0];
1697             }
1698            
1699 0           return map { $_ => $formdata->{ $_ } }
1700 0           grep { defined $formdata->{ $_ } } # don't search on unsubmitted fields
  0            
1701             @fields;
1702             }
1703              
1704             =item search_where_from_form
1705              
1706             L must be loaded in your
1707             CDBI class for this to work.
1708              
1709             If no search terms are specified, then the search
1710              
1711             WHERE 1 = 1
1712            
1713             is executed (returns all rows), no matter what search operator may have been selected.
1714              
1715             =cut
1716              
1717             sub search_where_from_form
1718             {
1719 0     0 1   my $class = shift;
1720            
1721 0 0         Carp::croak "search_where_from_form can only be called as a class method" if ref $class;
1722              
1723 0           __PACKAGE__->_run_search_where_from_form( $class, @_ );
1724             }
1725              
1726             # have a look at Maypole::Model::CDBI::search()
1727             sub _run_search_where_from_form
1728             {
1729 0     0     my ( $me, $them, $fb ) = @_;
1730            
1731 0 0 0       return unless $fb->submitted && $fb->validate;
1732              
1733 0           my %search_data = $me->_get_search_spec( $them, $fb );
1734            
1735             # clean out empty fields
1736 0 0         do { delete( $search_data{ $_ } ) unless $search_data{ $_ } } for keys %search_data;
  0            
1737            
1738             # these match fields added in search_form()
1739 0           my %modifiers = ( search_opt_cmp => 'cmp',
1740             search_opt_order_by => 'order_by',
1741             );
1742            
1743 0           my %search_modifiers = $me->_get_search_spec( $them, $fb, [ keys %modifiers ] );
1744            
1745             # rename modifiers for SQL::Abstract - taking care not to autovivify entries
1746             $search_modifiers{ $modifiers{ $_ } } = delete( $search_modifiers{ $_ } )
1747 0           for grep { $search_modifiers{ $_ } } keys %modifiers;
  0            
1748            
1749             # return everything if no search terms specified
1750 0 0         unless ( %search_data )
1751             {
1752 0           $search_data{1} = 1;
1753 0           $search_modifiers{cmp} = '=';
1754             }
1755            
1756 0 0         my @search = %search_modifiers ? ( \%search_data, \%search_modifiers ) : %search_data;
1757            
1758 0           return $them->search_where( @search );
1759             }
1760              
1761             =item find_or_create_from_form
1762              
1763             Does a C using submitted form data.
1764              
1765             =cut
1766            
1767             sub find_or_create_from_form
1768             {
1769 0     0 1   my $class = shift;
1770            
1771 0 0         Carp::croak "find_or_create_from_form can only be called as a class method" if ref $class;
1772              
1773 0           __PACKAGE__->_run_find_or_create_from_form( $class, @_ );
1774             }
1775              
1776             sub _run_find_or_create_from_form
1777             {
1778 0     0     my ( $me, $them, $fb ) = @_;
1779              
1780 0 0 0       return unless $fb->submitted && $fb->validate;
1781              
1782 0           my %search_data = $me->_get_search_spec( $them, $fb );
1783            
1784 0           return $them->find_or_create( \%search_data );
1785             }
1786              
1787             =item retrieve_or_create_from_form
1788              
1789             Attempts to look up an object. If none exists, a new object is created.
1790              
1791             This is similar to C, except that this method will not
1792             update pre-existing objects.
1793              
1794             =cut
1795              
1796             sub retrieve_or_create_from_form
1797             {
1798 0     0 1   my $class = shift;
1799            
1800 0 0         Carp::croak "retrieve_or_create_from_form can only be called as a class method" if ref $class;
1801              
1802 0           __PACKAGE__->_run_retrieve_or_create_from_form( $class, @_ );
1803             }
1804              
1805             sub _run_retrieve_or_create_from_form
1806             {
1807 0     0     my ( $me, $them, $fb ) = @_;
1808              
1809 0 0 0       return unless $fb->submitted && $fb->validate;
1810              
1811 0           my $object = $them->retrieve_from_form( $fb );
1812            
1813 0 0         return $object if $object;
1814            
1815 0           $them->create_from_form( $fb );
1816             }
1817              
1818              
1819             =back
1820              
1821             =head1 Automatic validation setup
1822              
1823             If you place a normal L validation spec in
1824             C<< $class->form_builder_defaults->{validate} >>, that spec will be used to configure validation.
1825              
1826             If there is no spec in C<< $class->form_builder_defaults->{validate} >>, then validation will
1827             be configured automatically. The default configuration is pretty basic, but you can modify it
1828             by placing settings in C<< $class->form_builder_defaults->{auto_validate} >>.
1829              
1830             You must load L in your class if using automatic
1831             validation.
1832              
1833             =over 4
1834              
1835             =item Basic auto-validation
1836              
1837             Given no validation options for a column in the C slot, the settings for most columns
1838             will be taken from C<%Class::DBI::FormBuilder::ValidMap>. This maps SQL column types (as supplied by
1839             L) to the L validation
1840             settings C, C, or C.
1841              
1842             MySQL C or C columns will be set up to validate that the submitted value(s) match the allowed
1843             values (although C column functionality requires the patch to CDBI::mysql mentioned above).
1844              
1845             Any column listed in C<< $class->form_builder_defaults->{options} >> will be set to validate those values.
1846              
1847             =item Advanced auto-validation
1848              
1849             The following settings can be placed in C<< $class->form_builder_defaults->{auto_validate} >>.
1850              
1851             =over 4
1852              
1853             =item validate
1854              
1855             Specify validate types for specific columns:
1856              
1857             validate => { username => [qw(nate jim bob)],
1858             first_name => '/^\w+$/', # note the
1859             last_name => '/^\w+$/', # single quotes!
1860             email => 'EMAIL',
1861             password => \&check_password,
1862             confirm_password => {
1863             javascript => '== form.password.value',
1864             perl => 'eq $form->field("password")'
1865             }
1866            
1867             This option takes the same settings as the C option to C
1868             (i.e. the same as would otherwise go in C<< $class->form_builder_defaults->{validate} >>).
1869             Settings here override any others.
1870              
1871             =item skip_columns
1872              
1873             List of columns that will not be validated:
1874              
1875             skip_columns => [ qw( secret_stuff internal_data ) ]
1876              
1877             =item match_columns
1878              
1879             Use regular expressions matching groups of columns to specify validation:
1880              
1881             match_columns => { qr/(^(widget|burger)_size$/ => [ qw( small medium large ) ],
1882             qr/^count_.+$/ => 'INT',
1883             }
1884            
1885             =item validate_types
1886              
1887             Validate according to SQL data types:
1888              
1889             validate_types => { date => \&my_date_checker,
1890             }
1891            
1892             Defaults are taken from the package global C<%TypesMap>.
1893            
1894             =item match_types
1895              
1896             Use a regular expression to map SQL data types to validation types:
1897              
1898             match_types => { qr(date) => \&my_date_checker,
1899             }
1900            
1901             =item debug
1902            
1903             Control how much detail to report (via C) during setup. Set to 1 for brief
1904             info, and 2 for a list of each column's validation setting.
1905              
1906             =item strict
1907              
1908             If set to 1, will die if a validation setting cannot be determined for any column.
1909             Default is to issue warnings and not validate these column(s).
1910            
1911             =back
1912              
1913             =item Validating relationships
1914              
1915             Although it would be possible to retrieve the IDs of all objects for a related column and use these to
1916             set up validation, this would rapidly become unwieldy for larger tables. Default validation will probably be
1917             acceptable in most cases, as the column type will usually be some kind of integer.
1918              
1919             =item timestamp
1920              
1921             The default behaviour is to skip validating C columns. A warning will be issued
1922             if the C parameter is set to 2.
1923              
1924             =item Failures
1925              
1926             The default mapping of column types to validation types is set in C<%Class::DBI::FormBulder::ValidMap>,
1927             and is probably incomplete. If you come across any failures, you can add suitable entries to the hash before calling C. However, B email me with any failures so the hash can be updated for everyone.
1928              
1929             =back
1930              
1931             =cut
1932              
1933             sub _get_type
1934             {
1935 0     0     my ( $me, $them, $col ) = @_;
1936            
1937 0           my $type = $them->column_type( $col );
1938            
1939 0 0         die "No type detected for column $col in $them" unless $type;
1940            
1941             # $type may be something like varchar(255)
1942            
1943 0           $type =~ s/[^a-z]*$//;
1944              
1945 0           return $type;
1946             }
1947            
1948             sub _valid_map
1949             {
1950 0     0     my ( $me, $type ) = @_;
1951            
1952 0           return $ValidMap{ $type };
1953             }
1954              
1955             sub _setup_auto_validation
1956             {
1957 0     0     my ( $me, $them, $fb_args ) = @_;
1958            
1959             # $fb_args is the args hash that will be sent to CGI::FB to construct the form.
1960             # Here we re-write $fb_args->{validate}
1961            
1962 0           my %args = $me->_get_auto_validate_args( $them );
1963            
1964 0 0         return unless %args;
1965            
1966 0 0         warn "auto-validating $them\n" if $args{debug};
1967            
1968             #warn "fb_args:" . Dumper( $fb_args );
1969            
1970 0   0       my $v_cols = $args{validate} || {};
1971 0   0       my $skip_cols = $args{skip_columns} || [];
1972 0   0       my $match_cols = $args{match_columns} || {};
1973 0   0       my $v_types = $args{validate_types} || {};
1974 0   0       my $match_types = $args{match_types} || {};
1975            
1976 0           my %skip = map { $_ => 1 } @$skip_cols;
  0            
1977            
1978 0           my %validate;
1979            
1980             # $col->name preserves case - stringifying doesn't
1981 0           foreach my $col ( @{ $fb_args->{fields} } )
  0            
1982             {
1983 0 0         next if $skip{ $col };
1984            
1985             # this will get added at the end
1986 0 0         next if $v_cols->{ $col };
1987            
1988             # look for columns with options
1989             # TODO - what about related columns? - do not want to add 10^6 db rows to validation
1990            
1991 0   0       my $options = $them->form_builder_defaults->{options} || {};
1992            
1993 0           my $o = $options->{ $col };
1994            
1995 0 0         unless ( $o )
1996             {
1997 0           my ( $series, undef ) = $me->_get_col_options_for_enumlike( $them, $col );
1998 0           $o = $series;
1999 0 0 0       warn "(Probably) setting validation to options (@$o) for $col in $them" if ( $args{debug} > 1 and @$o );
2000 0 0         undef( $o ) unless @$o;
2001             }
2002            
2003 0           my $type = $me->_get_type( $them, $col );
2004            
2005 0   0       my $v = $o || $v_types->{ $type };
2006            
2007 0           foreach my $regex ( keys %$match_types )
2008             {
2009 0 0         last if $v;
2010 0 0         $v = $match_types->{ $regex } if $type =~ $regex;
2011             }
2012            
2013 0           foreach my $regex ( keys %$match_cols )
2014             {
2015 0 0         last if $v;
2016 0 0         $v = $match_cols->{ $regex } if $col =~ $regex;
2017             }
2018            
2019 0   0       my $skip_ts = ( ( $type eq 'timestamp' ) && ! $v );
2020            
2021 0 0 0       warn "Skipping $them $col [timestamp]\n" if ( $skip_ts and $args{debug} > 1 );
2022            
2023 0 0         next if $skip_ts;
2024            
2025 0   0       $v ||= $me->_valid_map( $type ) || '';
      0        
2026            
2027 0 0         my $fail = "No validate type detected for column $col, type $type in $them"
2028             unless $v;
2029            
2030 0 0         $fail and $args{strict} ? die $fail : warn $fail;
    0          
2031            
2032 0           my $type2 = substr( $type, 0, 25 );
2033 0 0         $type2 .= '...' unless $type2 eq $type;
2034            
2035             warn sprintf "Untainting %s %s [%s] as %s\n", $them, $col, $type2, $v
2036 0 0         if $args{debug} > 1;
2037            
2038 0 0         $validate{ $col } = $v if $v;
2039             }
2040            
2041 0           my $validation = { %validate, %$v_cols };
2042            
2043 0 0         if ( $args{debug} > 1 )
2044             {
2045 0           Data::Dumper->require;
2046 0           warn "Setting up validation: " . Data::Dumper::Dumper( $validation );
2047             }
2048            
2049 0           $fb_args->{validate} = $validation;
2050            
2051             #use Data::Dumper;
2052             #warn Dumper( $validation );
2053            
2054 0           return;
2055             }
2056              
2057             sub _get_auto_validate_args
2058             {
2059 0     0     my ( $me, $them ) = @_;
2060            
2061 0           my $fb_defaults = $them->form_builder_defaults;
2062            
2063 0 0 0       if ( %{ $fb_defaults->{validate} || {} } && %{ $fb_defaults->{auto_validate} || {} } )
  0 0          
  0 0          
2064             {
2065 0           die "Got validation AND auto-validation settings in form_builder_defaults (should only have one or other)";
2066             }
2067            
2068 0 0         return if %{ $fb_defaults->{validate} || {} };
  0 0          
2069            
2070             #use Data::Dumper;
2071             #warn "automating with config " . Dumper( $fb_defaults->{auto_validate} );
2072            
2073             # stop lots of warnings, and ensure something is set so the cfg exists test passes
2074 0   0       $fb_defaults->{auto_validate}->{debug} ||= 0;
2075            
2076 0           return %{ $fb_defaults->{auto_validate} };
  0            
2077             }
2078              
2079             =head1 Plugins
2080              
2081             C relationships can refer to non-CDBI classes. In this case, C will attempt to
2082             load (via C) an appropriate plugin. For instance, for a C column, it will attempt
2083             to load C. Then it will call the C method in the plugin, passing
2084             the CDBI class for whom the form has been constructed, the form, and the name of the field being processed.
2085             The plugin can use this information to modify the form, perhaps adding extra fields, or controlling
2086             stringification, or setting up custom validation.
2087              
2088             If no plugin is found, a fatal exception is raised. If you have a situation where it would be useful to
2089             simply stringify the object instead, let me know and I'll make this configurable.
2090              
2091             =head1 TODO
2092              
2093             Better merging of attributes. For instance, it'd be nice to set some field attributes
2094             (e.g. size or type) in C, and not lose them when the fields list is
2095             generated and added to C<%args>.
2096              
2097             Store CDBI errors somewhere on the form. For instance, if C fails because
2098             no object could be retrieved using the form data.
2099              
2100             Detect binary data and build a file upload widget.
2101              
2102             C relationships.
2103              
2104             C and C equivalent column types in other dbs.
2105              
2106             Think about non-CDBI C inflation/deflation. In particular, maybe there's a Better
2107             Way than subclassing to add C methods. For instance, adding a date-picker widget
2108             to deal with DateTime objects. B: the new plugin architecture added in 0.32 should
2109             handle this.
2110              
2111             Figure out how to build a form for a related column when starting from a class, not an object
2112             (pointed out by Peter Speltz). E.g.
2113              
2114             my $related = $object->some_col;
2115              
2116             print $related->as_form->render;
2117            
2118             will not work if $object is a class. Have a look at Maypole::Model::CDBI::related_class.
2119              
2120             Integrate fields from a related class object into the same form (e.g. show address fields
2121             in a person form, where person has_a address). B: fairly well along in 0.32 (C).
2122              
2123             C<_splice_form> needs to handle custom setup for more relationship types.
2124              
2125             =head1 AUTHOR
2126              
2127             David Baird, C<< >>
2128              
2129             =head1 BUGS
2130              
2131             Please report any bugs or feature requests to
2132             C, or through the web interface at
2133             L.
2134             I will be notified, and then you'll automatically be notified of progress on
2135             your bug as I make changes.
2136              
2137             Looking at the code (0.32), I suspect updates to has_many accessors are not implemented, since the update
2138             methods only fetch data for columns( 'All' ), which doesn't include has_many accessors/mutators.
2139              
2140             =head1 ACKNOWLEDGEMENTS
2141              
2142             James Tolley for providing the plugin code.
2143              
2144             =head1 COPYRIGHT & LICENSE
2145              
2146             Copyright 2005 David Baird, All Rights Reserved.
2147              
2148             This program is free software; you can redistribute it and/or modify it
2149             under the same terms as Perl itself.
2150              
2151             =cut
2152              
2153             1; # End of Class::DBI::Plugin::FormBuilder
2154              
2155             __END__