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, C or
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__