]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/Model/CDBI/AsForm.pm
cd295f3e06335e502cbd720839ee3625f688608c
[maypole.git] / lib / Maypole / Model / CDBI / AsForm.pm
1 package Maypole::Model::CDBI::AsForm;
2
3 #TODO -- 
4
5 # TESTED and Works --
6 #  has_many select -- $obj->to_field($has_many_col);   # select one form many
7 #                  -- $class->to_field($has_many_col); # foreign inputs  
8 #  $class->search_inputs; /
9
10
11 use strict;
12 use warnings;
13
14 use base 'Exporter';
15 use Data::Dumper;
16 use Class::DBI::Plugin::Type ();
17 use HTML::Element;
18 use Carp qw/cluck/;
19
20 our $OLD_STYLE = 0;
21 # pjs  --  Added new methods to @EXPORT 
22 our @EXPORT = 
23         qw( 
24                 to_cgi to_field  foreign_input_delimiter search_inputs unselect_element
25                 _field_from_how _field_from_relationship _field_from_column
26                 _to_textarea _to_textfield _to_select  _select_guts
27                 _to_foreign_inputs _to_enum_select _to_bool_select
28                 _to_hidden _to_link_hidden _rename_foreign_input _to_readonly
29                 _options_from_objects _options_from_arrays _options_from_hashes 
30                 _options_from_array _options_from_hash 
31     );
32
33 our $VERSION = '.95'; 
34
35 =head1 NAME
36
37 Maypole::Model:CDBI::AsForm - Produce HTML form elements for database columns
38
39 =head1 SYNOPSIS
40
41     package Music::CD;
42     use Maypole::Model::CDBI::AsForm;
43     use base 'Class::DBI';
44     use CGI;
45     ...
46
47     sub create_or_edit {
48         my $self = shift;
49         my %cgi_field = $self->to_cgi;
50         return start_form,
51                (map { "<b>$_</b>: ". $cgi_field{$_}->as_HTML." <br>" } 
52                     $class->Columns),
53                end_form;
54     }
55
56
57    . . .
58
59     # Somewhere else in a Maypole application about beer...
60
61
62
63
64    $beer->to_field('brewery', 'textfield', { 
65                 name => 'brewery_id', value => $beer->brewery,
66                 # however, no need to set value since $beer is object
67    });
68
69    # Rate a beer
70    $beer->to_field(rating =>  select => {
71                 items => [1 , 2, 3, 4, 5],
72    });
73
74    # Select a Brewery to visit in the UK
75    Brewery->to_field(brewery_id => {
76                 items => [ Brewery->search_like(location => 'UK') ],
77    });
78
79   # Make a select for a boolean field
80   $Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); 
81
82    $beer->to_field('brewery', {
83                 selected => $beer->brewery, # again not necessary since caller is obj.
84    });
85
86
87     $beer->to_field('brewery', 'link_hidden', {r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery});
88     # an html link that is also a hidden input to the object. R is required to
89     # make the uri  unless you  pass a  uri
90
91
92
93     #####################################################
94     # Templates Usage
95
96     <form ..>
97
98     ...
99
100     <label>
101
102      <span class="field"> [% classmetadata.colnames.$col %] : </span>
103
104      [% object.to_field(col).as_XML %]
105
106     </label>
107
108     . . .
109
110     <label>
111
112      <span class="field"> Brewery : </span>
113
114      [% object.to_field('brewery', { selected => 23} ).as_XML %]
115
116     </label>
117
118     . . .
119
120     </form>
121
122
123     #####################################################
124     # Advanced Usage
125
126     # has_many select
127     package Job;
128     __PACKAGE__->has_a('job_employer' => 'Employer');
129     __PACKAGE__->has_a('contact'  => 'Contact')
130
131     package Contact;
132     __PACKAGE__->has_a('cont_employer' => 'Employer');
133     __PACKAGE__->has_many('jobs'  => 'Job',
134                           { join => { job_employer => 'cont_employer' },
135                             constraint => { 'finshed' => 0  },
136                             order_by   => "created ASC",
137                           }
138                          );
139
140     package Employer;
141     __PACKAGE__->has_many('jobs'  => 'Job',);
142     __PACKAGE__->has_many('contacts'  => 'Contact',
143                           order_by => 'name DESC',
144                          );
145
146
147   # Choose some jobs to add to a contact (has multiple attribute).
148   my $job_sel = Contact->to_field('jobs'); # Uses constraint and order by
149
150
151   # Choose a job from $contact->jobs 
152   my $job_sel = $contact->to_field('jobs');
153
154   1;
155
156
157
158
159 =head1 DESCRIPTION
160
161 This module helps to generate HTML forms for creating new database rows
162 or editing existing rows. It maps column names in a database table to
163 HTML form elements which fit the schema. Large text fields are turned
164 into textareas, and fields with a has-a relationship to other
165 C<Class::DBI> tables are turned into select drop-downs populated with
166 objects from the joined class.
167
168
169 =head1 ARGUMENTS HASH
170
171 This provides a convenient way to tweak AsForm's behavior in exceptional or 
172 not so exceptional instances. Below describes the arguments hash and 
173 example usages. 
174
175
176   $beer->to_field($col, $how, $args); 
177   $beer->to_field($col, $args);
178
179 Not all _to_* methods pay attention to all arguments. For example, '_to_textfield' does not look in $args->{'items'} at all.
180
181 =over
182
183 =item name -- the name the element will have , this trumps the derived name.
184
185   $beer->to_field('brewery', 'readonly', {
186                 name => 'brewery_id'
187   });
188
189 =item value -- the initial value the element will have, trumps derived value
190
191   $beer->to_field('brewery', 'textfield', { 
192                 name => 'brewery_id', value => $beer->brewery,
193                 # however, no need to set value since $beer is object
194   });
195
196 =item items -- array of items generally used to make select box options
197
198 Can be array of objects, hashes, arrays, or strings, or just a hash.
199
200    # Rate a beer
201    $beer->to_field(rating =>  select => {
202                 items => [1 , 2, 3, 4, 5],
203    });
204
205    # Select a Brewery to visit in the UK
206    Brewery->to_field(brewery_id => {
207                 items => [ Brewery->search_like(location => 'UK') ],
208    });
209
210   # Make a select for a boolean field
211   $Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); 
212
213 =item selected -- something representing which item is selected in a select box
214
215    $beer->to_field('brewery', {
216                 selected => $beer->brewery, # again not necessary since caller is obj.
217    });
218
219 Can be an simple scalar id, an object, or an array of either
220
221 =item class -- the class for which the input being made for field pertains to.
222
223 This in almost always derived in cases where it may be difficult to derive, --
224    # Select beers to serve on handpump
225    Pub->to_field(handpumps => select => {
226                 class => 'Beer', order_by => 'name ASC', multiple => 1,
227         });
228
229 =item column_type -- a string representing column type
230
231   $pub->to_field('open', 'bool_select', {
232                 column_type => "bool('Closed', 'Open'),
233   });
234
235 =item column_nullable -- flag saying if column is nullable or not
236
237 Generally this can be set to get or not get a null/empty option added to
238 a select box.  AsForm attempts to call "$class->column_nullable" to set this
239 and it defaults to true if there is no shuch method.
240
241   $beer->to_field('brewery', { column_nullable => 1 });    
242
243 =item r or request  -- the Mapyole request object 
244
245 =item uri -- uri for a link , used in methods such as _to_link_hidden
246
247  $beer->to_field('brewery', 'link_hidden', 
248           {r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery}); 
249  # an html link that is also a hidden input to the object. R is required to
250  # make the uri  unless you  pass a  uri
251
252 =item order_by, constraint, join
253
254 These are used in making select boxes. order_by is a simple order by clause
255 and constraint and join are hashes used to limit the rows selected. The
256 difference is that join uses methods of the object and constraint uses 
257 static values. You can also specify these in the relationship definitions.
258 See the relationships documentation of how to set arbitrayr meta info. 
259
260   BeerDB::LondonBeer->has_a('brewery', 'BeerDB::Brewery', 
261                    order_by     => 'brewery_name ASC',
262            constraint   => {location  => 'London'},
263            'join'       => {'brewery_tablecolumn  => 'beer_obj_column'}, 
264           );
265
266 =item no_hidden_constraints -- 
267
268 Tell AsForm not to make hidden inputs for relationship constraints. It does
269 this  sometimes when making foreign inputs. However, i think it should not
270 do this and that the FromCGI 's _create_related method should do it. 
271
272 =back
273
274 =head2 to_cgi
275
276   $self->to_cgi([@columns, $args]); 
277
278 This returns a hash mapping all the column names to HTML::Element objects 
279 representing form widgets.  It takes two opitonal arguments -- a list of 
280 columns and a hashref of hashes of arguments for each column.  If called with an object like for editing, the inputs will have the object's values.
281
282   $self->to_cgi(); # uses $self->columns;  # most used
283   $self->to_cgi(qw/brewery style rating/); # sometimes
284   # and on rare occassions this is desireable if you have a lot of fields
285   # and dont want to call to_field a bunch of times just to tweak one or 
286   # two of them.
287   $self->to_cgi(@cols, {brewery => {  
288                                                                          how => 'textfield' # too big for select 
289                                                                    }, 
290                                                 style   => { 
291                                                                          column_nullable => 0, 
292                                                                          how => 'select', 
293                                                                          items => ['Ale', 'Lager']
294                                                                    }
295                                                 });
296
297 =cut
298
299 sub to_cgi {
300                 my ($class, @columns) = @_; # pjs -- added columns arg
301                 my $args = {};
302                 if (not @columns) {
303                                 @columns = $class->columns;
304                                 # Eventually after stabalization, we could add display_columns 
305                                 #keys map { $_ => 1 } ($class->display_columns, $class->columns); 
306                 }
307                 else {
308                                 if ( ref $columns[-1] eq 'HASH' ) { $args = pop @columns; }
309                 }
310                 map { $_ => $class->to_field($_, $args->{$_}) } @columns;
311 }
312
313 =head2 to_field($field [, $how][, $args])
314
315 This maps an individual column to a form element. The C<how> argument
316 can be used to force the field type into any you want. All that you need 
317 is a method named "_to_$how" in your class. Your class inherits many from
318 AsForm  already. 
319
320 If C<how> is specified but the class cannot call the method it maps to,
321 then AsForm will issue a warning and the default input will be made. 
322 You can write your own "_to_$how" methods and AsForm comes with many.
323 See C<HOW Methods>. You can also pass this argument in $args->{how}.
324
325
326 =cut
327
328 sub to_field {
329                 my ($self, $field, $how, $args) = @_;
330                 if (ref $how)   { $args = $how; $how = ''; }
331                 unless ($how)   { $how = $args->{how} || ''; }
332 #warn "In to_field field is $field how is $how. args ar e" . Dumper($args) . " \n";
333                 # Set sensible default value
334                 if  ($field and not defined $args->{default}) { 
335                                 my $def = $self->column_default($field) ;
336                                 # exclude defaults we don't want actually put as value for input
337                                 if (defined $def) {
338                                                 $def = $def =~ /(^0000-00-00.*$|^0[0]*$|^0\.00$|CURRENT_TIMESTAMP|NULL)/i ? '' : $def ;
339                                                 $args->{default} = $def;
340                                 }
341                 }
342
343
344
345                 return  $self->_field_from_how($field, $how, $args)   || 
346                 $self->_field_from_relationship($field, $args) ||
347                 $self->_field_from_column($field, $args)  ||
348                 $self->_to_textfield($field, $args);
349 }
350
351
352 =head2 search_inputs
353
354   my $cgi = $class->search_inputs ([$args]); # optional $args
355
356 Returns hash or hashref of search inputs elements for a class making sure the
357 inputs are empty of any initial values.
358 You can specify what columns you want inputs for in
359 $args->{columns} or
360 by the method "search_columns". The default is  "display_columns".
361 If you want to te search on columns in related classes you can do that by
362 specifying a one element hashref in place of the column name where
363 the key is the related "column" (has_a or has_many method for example) and
364 the value is a list ref of columns to search on in the related class.
365
366 Example:
367   sub  BeerDB::Beer::search_columns {
368          return ( 'name' , 'rating', { brewery => [ 'name', 'location'] } );
369   }
370
371   # Now foreign inputs are made for Brewery name and location and the
372   # there will be no name clashing and processing can be automated.
373
374 =cut
375
376
377 sub search_inputs {
378                 my ($class, $args) = @_;
379                 $class = ref $class || $class;
380                 #my $accssr_class = { $class->accessor_classes };
381                 my %cgi;
382
383                 $args->{columns} ||= $class->can('search_columns') ?[$class->search_columns] : [$class->display_columns];
384
385                 foreach my $field ( @{ $args->{columns} } ) {
386                                 my $base_args = {
387                                                 no_hidden_constraints => 1,
388                                                 column_nullable => 1, # empty option on select boxes
389                                                 value  => '',
390                                 };
391                                 if ( ref $field eq "HASH" ) { # foreign search fields
392                                                 my ($accssr, $cols)  = each %$field;
393                                                 $base_args->{columns} = $cols;
394                                                 unless (  @$cols ) {
395                                                                 # default to search fields for related
396                                                                 #$cols =  $accssr_class->{$accssr}->search_columns;
397                                                                 die ("$class search_fields error: Must specify at least one column to search in the foreign object named '$accssr'");
398                                                 }
399                                                 my $fcgi  = $class->to_field($accssr, 'foreign_inputs', $base_args);
400
401                                                 # unset the default values for a select box
402                                                 foreach (keys %$fcgi) {
403                                                                 my $el = $fcgi->{$_};
404                                                                 if ($el->tag eq 'select') {
405
406                                                                                 $class->unselect_element($el);
407                                                                                 my ($first, @content) = $el->content_list;
408                                                                                 my @fc = $first->content_list;
409                                                                                 my $val = $first ? $first->attr('value') : undef;  
410                                                                                 if ($first and (@fc > 0 or (defined $val and $val ne '')) ) { # something ( $first->attr('value') ne '' or 
411
412                                                                                                 #(defined $first->attr('value') or $first->attr('value') ne ''))  
413                                                                                                 # push an empty option on stactk
414                                                                                                 $el->unshift_content(HTML::Element->new('option'));
415                                                                                 }
416                                                                 }
417
418                                                 }
419                                                 $cgi{$accssr} = $fcgi;
420                                                 delete $base_args->{columns};
421                                 }
422                                 else {
423                                                 $cgi{$field} = $class->to_field($field, $base_args); #{no_select => $args->{no_select}{$field} });
424                                                 my $el = $cgi{$field};
425                                                 if ($el->tag eq 'select') {
426                                                                 $class->unselect_element($el);
427                                                                 my ($first, @content) = $el->content_list;
428                                                                 if ($first and $first->content_list) { # something 
429                                                                                 #(defined $first->attr('value') or $first->attr('value') ne ''))  
430                                                                                 # push an empty option on stactk
431                                                                                 $el->unshift_content(HTML::Element->new('option'));
432                                                                 }
433                                                 }
434                                 }
435                 }
436                 return \%cgi;
437 }
438
439
440
441
442 =head2 unselect_element
443
444   unselect any selected elements in a HTML::Element select list widget
445
446 =cut
447 sub unselect_element {
448                 my ($self, $el) = @_;
449                 #unless (ref $el eq 'HTML::Element') {
450                 #$self->_croak ('Need an HTML::Element to unselect. You gave a ' . Dumper($el)); }
451                 if ($el->tag eq 'select') {
452                                 foreach my $opt ($el->content_list) {
453                                                 $opt->attr('selected', undef) if $opt->attr('selected');
454                                 }
455                 }
456 }
457
458 =head2 _field_from_how($field, $how,$args)
459
460 Returns an input element based the "how" parameter or nothing at all.
461 Override at will. 
462
463 =cut
464
465 sub _field_from_how {
466                 my ($self, $field, $how, $args) = @_;
467                 return unless $how;
468                 $args ||= {};
469                 no strict 'refs';
470                 my $meth = "_to_$how";
471                 if (not $self->can($meth)) { 
472                                 warn "Class can not $meth";
473                                 return;
474                 }
475                 return $self->$meth($field, $args); 
476                 return;
477 }
478
479 =head2 _field_from_relationship($field, $args)
480
481 Returns an input based on the relationship associated with the field or nothing.
482 Override at will.
483
484 For has_a it will give select box
485
486 =cut
487
488 sub _field_from_relationship {
489                 my ($self, $field, $args) = @_;
490                 return unless $field;
491                 my $rel_meta = $self->related_meta('r',$field) || return; 
492                 my $rel_name = $rel_meta->{name};
493                 #my $meta = $self->meta_info;
494                 #grep{ defined $meta->{$_}{$field} } keys %$meta;
495                 my $fclass = $rel_meta->foreign_class;
496                 my $fclass_is_cdbi = $fclass ? $fclass->isa('Class::DBI') : 0;
497
498                 # maybe has_a select 
499                 if ($rel_meta->{name} eq 'has_a' and $fclass_is_cdbi) {
500                                 # This condictions allows for trumping of the has_a args
501                                 if  (not $rel_meta->{args}{no_select} and not $args->{no_select}) 
502                                 {
503                                                 $args->{class} = $fclass;
504                                                 return  $self->_to_select($field, $args);
505                                 }
506                                 return;
507                 }
508                 # maybe has many select
509                 if ($rel_meta->{name} eq 'has_many' and $fclass_is_cdbi and ref $self) {
510                                 # This condictions allows for trumping of the has_a args
511                                 if  (not $rel_meta->{args}{no_select} and not $args->{no_select}) 
512                                 {
513                                                 $args->{class} = $fclass;
514                                                 my @itms = $self->$field; # need list not iterator
515                                                 $args->{items} = \@itms;
516                                                 return  $self->_to_select($field, $args);
517                                 }
518                                 return;
519                 }
520
521                 # maybe foreign inputs 
522                 my %local_cols = map { $_ => 1 } $self->columns; # includes is_a cols
523                 if ($fclass_is_cdbi and (not $local_cols{$field} or $rel_name eq 'has_own'))
524                 {
525                                 $args->{related_meta} = $rel_meta; # suspect faster to set these args 
526                                 return $self->_to_foreign_inputs($field, $args);
527                 }
528                 return;
529 }
530
531 =head2 _field_from_column($field, $args)
532
533 Returns an input based on the column's characteristics, namely type, or nothing.
534 Override at will.
535
536 =cut
537
538 sub _field_from_column {
539                 my ($self, $field, $args) = @_;
540                 # this class and pk are default class and field at this point
541                 my $class = $args->{class} || $self;
542                 $class = ref $class || $class;
543                 $field  ||= ($class->primary_columns)[0]; # TODO
544
545                 # Get column type       
546                 unless ($args->{column_type}) { 
547                                 if ($class->can('column_type')) {
548                                                 $args->{column_type} = $class->column_type($field);
549                                 }       
550                                 else {
551                                                 # Right, have some of this
552                                                 eval "package $class; Class::DBI::Plugin::Type->import()";
553                                                 $args->{column_type} = $class->column_type($field);
554                                 }
555                 }
556                 my $type = $args->{column_type};
557
558                 return $self->_to_textfield($field, $args)
559                 if $type  and $type =~ /^(VAR)?CHAR/i;  #common type
560                 return $self->_to_textarea($field, $args)
561                 if $type and $type =~ /^(TEXT|BLOB)$/i;
562                 return $self->_to_enum_select($field, $args)  
563                 if $type and  $type =~ /^ENUM\((.*?)\)$/i; 
564                 return $self->_to_bool_select($field, $args)
565                 if $type and  $type =~ /^BOOL/i; 
566                 return $self->_to_readonly($field, $args)
567                 if $type and $type =~ /^readonly$/i;
568                 return;
569 }
570
571
572 sub _to_textarea {
573                 my ($self, $col, $args) = @_;
574                 my $class = $args->{class} || $self;
575                 $class = ref $class || $class;
576                 $col  ||= ($class->primary_columns)[0]; # TODO
577                 # pjs added default     
578                 $args ||= {};
579                 my $val =  $args->{value}; 
580
581                 unless (defined $val) {
582                                 if (ref $self) {
583                                                 $val = $self->$col; 
584                                 }
585                                 else { 
586                                                 $val = $args->{default}; 
587                                                 $val = '' unless defined $val;  
588                                 }
589                 }
590                 my ($rows, $cols) = _box($val);
591                 $rows = $args->{rows} if $args->{rows};
592                 $cols = $args->{cols} if $args->{cols};;
593                 my $name = $args->{name} || $col; 
594                 my $a =
595                 HTML::Element->new("textarea", name => $name, rows => $rows, cols => $cols);
596                 $a->push_content($val);
597                 $OLD_STYLE && return $a->as_HTML;
598                 $a;
599 }
600
601 sub _to_textfield {
602                 my ($self, $col, $args ) = @_;
603                 use Carp qw/confess/;
604                 confess "No col passed to _to_textfield" unless $col;
605                 $args ||= {};
606                 my $val  = $args->{value}; 
607                 my $name = $args->{name} || $col; 
608
609                 unless (defined $val) {
610                                 if (ref $self) {
611                                                 # Case where column inflates.
612                                                 # Input would get stringification which could be not good.
613                                                 #  as in the case of Time::Piece objects
614                                                 $val = $self->can($col) ? $self->$col : ''; # in case it is a virtual column
615                                                 if (ref $val) {
616                                                                 if (my $meta = $self->related_meta('',$col)) {
617                                                                                 if (my $code = $meta->{args}{deflate4edit} || $meta->{args}{deflate} ) {
618                                                                                                 $val  = ref $code ? &$code($val) : $val->$code;
619                                                                                 }
620                                                                                 elsif ( $val->isa('Class::DBI') ) {
621                                                                                                 $val  = $val->id;
622                                                                                 }
623                                                                                 else { 
624                                                                                                 #warn "No deflate4edit code defined for $val of type " . 
625                                                                                                 #ref $val . ". Using the stringified value in textfield..";
626                                                                                 }
627                                                                 }
628                                                                 else {
629                                                                                 $val  = $val->id if $val->isa("Class::DBI"); 
630                                                                 }
631                                                 }
632
633                                 }
634                                 else {
635                                                 $val = $args->{default}; 
636                                                 $val = '' unless defined $val;
637                                 }
638                 }
639                 my $a;
640                 # THIS If section is neccessary or you end up with "value" for a vaiue
641                 # if val is 
642                 $val = '' unless defined $val; 
643                 $a = HTML::Element->new("input", type => "text", name => $name, value =>$val);
644                 $OLD_STYLE && return $a->as_HTML;
645                 $a;
646 }
647
648
649 # Old version
650 #sub _to_select {
651 #       my ($self, $col, $hint) = @_;
652 #       my $fclass = $hint || $self->__hasa_rels->{$col}->[0];
653 #       my @objs        = $fclass->retrieve_all;
654 #       my $a           = HTML::Element->new("select", name => $col);
655 #       for (@objs) {
656 #               my $sel = HTML::Element->new("option", value => $_->id);
657 #               $sel->attr("selected" => "selected")
658 #                       if ref $self
659 #                       and eval { $_->id eq $self->$col->id };
660 #               $sel->push_content($_->stringify_self);
661 #               $a->push_content($sel);
662 #       }
663 #       $OLD_STYLE && return $a->as_HTML;
664 #       $a;
665 #}
666
667
668
669
670 =head2 recognized arguments
671
672   selected => $object|$id,
673   name     => $name,
674   value    => $value,
675   where    => SQL 'WHERE' clause,
676   order_by => SQL 'ORDER BY' clause,
677   constraint => hash of constraints to search
678   limit    => SQL 'LIMIT' clause,
679   items    => [ @items_of_same_type_to_select_from ],
680   class => $class_we_are_selecting_from
681   stringify => $stringify_coderef|$method_name
682
683
684
685
686 # select box requirements
687 # 1. a select box for objecs of a has_a related class -- DONE 
688 =head2  1. a select box out of a has_a or has_many related class.
689   # For has_a the default behavior is to make a select box of every element in 
690   # related class and you choose one. 
691   #Or explicitly you can create one and pass options like where and order
692   BeerDB::Beer->to_field('brewery','select', {where => "location = 'Germany'");
693
694   # For has_many the default is to get a multiple select box with all objects.
695   # If called as an object method, the objects existing ones will be selected. 
696   Brewery::BeerDB->to_field('beers','select', {where => "rating > 5"}); 
697
698
699 =head2  2. a select box for objects of arbitrary class -- say BeerDB::Beer for fun. 
700   # general 
701   BeerDB::Beer->to_field('', 'select', $options)
702
703   BeerDB::Beer->to_field('', 'select'); # Select box of all the rows in class
704                                                                   # with PK as ID, $Class->to_field() same.
705   BeerDB::Beer->to_field('','select',{ where => "rating > 3 AND class like 'Ale'", order_by => 'rating DESC, beer_id ASC' , limit => 10});
706   # specify exact where clause 
707
708 =head2 3. If you already have a list of objects to select from  -- 
709
710   BeerDB:;Beer->to_field($col, 'select' , {items => $objects});
711
712 # 3. a select box for arbitrary set of objects 
713  # Pass array ref of objects as first arg rather than field 
714  $any_class_or_obj->to_field([BeerDB::Beer->search(favorite => 1)], 'select',);
715
716
717 =cut
718
719 sub _to_select {
720                 my ($self, $col, $args) = @_;
721                 $args ||= {};
722                 # Do we have items already ? Go no further. 
723                 if ($args->{items} and ref $args->{items}) {  
724                                 my $a = $self->_select_guts($col,  $args);
725         $OLD_STYLE && return $a->as_HTML;
726                 if ($args->{multiple}) { $a->attr('multiple', 'multiple');}
727                 return $a;
728         }
729         
730         # Proceed with work
731
732         my $rel_meta;  
733     if (not $col) { 
734                 unless ($args->{class}) {
735                 $args->{class} = ref $self || $self;
736                         # object selected if called with one
737             $args->{selected} = { $self->id => 1} 
738                                 if not $args->{selected} and ref $self;
739                 }
740         $col = $args->{class}->primary_column;
741                 $args->{name} ||= $col;
742     }
743     # Related Class maybe ? 
744     elsif ($rel_meta =  $self->related_meta('r:)', $col) ) {
745         $args->{class} = $rel_meta->{foreign_class};
746         # related objects pre selected if object
747                                 
748                 # "Has many" -- Issues:
749                 # 1) want to select one  or many from list if self is an object
750                 # Thats about all we can do really, 
751                 # 2) except for mapping which is TODO and  would 
752                 # do something like add to and take away from list of permissions for
753                 # example.
754
755                 # Hasmany select one from list if ref self
756                 if ($rel_meta->{name} =~ /has_many/i and ref $self) {
757                     my @itms =  $self->$col; # need list not iterator
758                         $args->{items} = \@itms;
759                         my $a = $self->_select_guts($col,  $args);
760                     $OLD_STYLE && return $a->as_HTML;
761                     return $a;
762                 }
763                 else {
764                         $args->{selected} ||= [ $self->$col ] if  ref $self; 
765                         #warn "selected is " . Dumper($args->{selected});
766                         my $c = $rel_meta->{args}{constraint} || {};
767                         my $j = $rel_meta->{args}{join} || {};
768                         my @join ; 
769                         if (ref $self) {
770                                 @join   =  map { $_ ." = ". $self->_attr($_) } keys %$j; 
771                         }
772                         my @constr= map { "$_ = '$c->{$_}'"} keys %$c; 
773                         $args->{where}    ||= join (' AND ', (@join, @constr));
774                         $args->{order_by} ||= $rel_meta->{args}{order_by};
775                         $args->{limit}    ||= $rel_meta->{args}{limit};
776                 }
777                         
778     }
779     # We could say :Col is name and we are selecting  out of class arg.
780         # DIE for now
781         #else {
782         #       die "Usage _to_select. $col not related to any class to select from. ";
783                 
784         #}
785                 
786     # Set arguments 
787         unless ( defined  $args->{column_nullable} ) {
788             $args->{column_nullable} = $self->can('column_nullable') ?
789                          $self->column_nullable($col) : 1;
790         }
791
792         # Get items to select from
793     my $items = _select_items($args); # array of hashrefs 
794
795         # Turn items into objects if related 
796         if ($rel_meta and not $args->{no_construct}) { 
797                 my @objs = ();
798                 push @objs, $rel_meta->{foreign_class}->construct($_) foreach @$items;
799                 $args->{items} = \@objs; 
800         }
801         else { $args->{items} = $items; } 
802         
803         #use Data::Dumper;
804         #warn "Just got items. They are  " . Dumper($args->{items});
805
806         # Make select HTML element
807         $a = $self->_select_guts($col, $args);
808
809         if ($args->{multiple}) {$a->attr('multiple', 'multiple');}
810
811         # Return 
812     $OLD_STYLE && return $a->as_HTML;
813     $a;
814
815 }
816
817
818 ##############
819 # Function # 
820 # #############
821 # returns the intersection of list refs a and b
822 sub _list_intersect {
823         my ($a, $b) = @_;
824         my %isect; my %union;
825     foreach my $e (@$a, @$b) { $union{$e}++ && $isect{$e}++ }
826         return  %isect;
827 }
828 ############
829 # FUNCTION #
830 ############
831 # Get Items  returns array of hashrefs
832 sub _select_items { 
833         my $args = shift;
834         my $fclass = $args->{class};
835     my @disp_cols = @{$args->{columns} || []};
836     @disp_cols = $fclass->columns('SelectBox') unless @disp_cols;
837     @disp_cols = $fclass->columns('Stringify')unless @disp_cols;
838     @disp_cols = $fclass->_essential unless @disp_cols;
839         unshift @disp_cols,  $fclass->columns('Primary');
840         #my %isect = _list_intersect(\@pks, \@disp_cols);
841         #foreach (@pks) { push @sel_cols, $_ unless $isect{$_}; } 
842     #push @sel_cols, @disp_cols;                
843
844         #warn "in select items. args are : " . Dumper($args);
845         my $distinct = '';
846         if ($args->{'distinct'}) {
847         $distinct = 'DISTINCT ';
848         }
849
850     my $sql = "SELECT $distinct" . join( ', ', @disp_cols) . 
851                   " FROM " . $fclass->table;
852
853         $sql .= " WHERE " . $args->{where}   if $args->{where};
854         $sql .= " ORDER BY " . $args->{order_by} if $args->{order_by};
855         $sql .= " LIMIT " . $args->{limit} if $args->{limit};
856         #warn "_select_items sql is : $sql";
857
858         my $sth = $fclass->db_Main->prepare($sql);
859         $sth->execute;
860         my @data;
861         while ( my $d = $sth->fetchrow_hashref ) {push @data, $d};  
862         return \@data;
863
864 }
865
866
867 # Makes a readonly input box out of column's value
868 # No args makes object to readonly
869 sub _to_readonly {
870     my ($self, $col, $args) = @_;
871     my $val = $args->{value};
872     if (not defined $val ) { # object to readonly
873         $self->_croak("AsForm: To readonly field called as class method without a value") unless ref $self; 
874         $val = $self->id;
875         $col = $self->primary_column;
876     }
877     my $a = HTML::Element->new('input', 'type' => 'text', readonly => '1',
878         'name' => $col, 'value'=>$val);
879         $OLD_STYLE && return $a->as_HTML;
880     $a;
881 }
882
883
884 =head2 _to_enum_select
885
886 Returns a select box for the an enum column type. 
887
888 =cut
889
890 sub _to_enum_select {
891     my ($self, $col, $args) = @_;
892         my $type = $args->{column_type};
893     $type =~ /ENUM\((.*?)\)/i;
894     (my $enum = $1) =~ s/'//g;
895     my @enum_vals = split /\s*,\s*/, $enum;
896
897     # determine which is pre selected --
898     my $selected = eval { $self->$col  };
899     $selected = $args->{default} unless defined $selected;
900     $selected = $enum_vals[0] unless defined $selected;
901
902     my $a = HTML::Element->new("select", name => $col);
903     for ( @enum_vals ) {
904         my $sel = HTML::Element->new("option", value => $_);
905         $sel->attr("selected" => "selected") if $_ eq $selected ;
906         $sel->push_content($_);
907         $a->push_content($sel);
908     }
909     $OLD_STYLE && return $a->as_HTML;
910     $a;
911 }
912
913
914 =head2 _to_bool_select
915
916 Returns a "No/Yes"  select box for a boolean column type. 
917
918 =cut
919 # TCODO fix this mess with args
920 sub _to_bool_select {
921     my ($self, $col, $args) = @_;
922         my $type = $args->{column_type};
923         my @bool_text = ('No', 'Yes');  
924         if ($type =~ /BOOL\((.+?)\)/i) {
925                 (my $bool = $1) =~ s/'//g;
926                 @bool_text = split /,/, $bool;
927         }
928
929         # get selected 
930         
931         my $selected = $args->{value} if defined $args->{value};
932         $selected = $args->{selected} unless defined $selected;
933         $selected =  ref $self ? eval {$self->$col;} : $args->{default}
934                 unless (defined $selected);
935
936     my $a = HTML::Element->new("select", name => $col);
937     if ($args->{column_nullable} || $args->{value} eq '') {
938                 my $null =  HTML::Element->new("option");
939                 $null->attr('selected', 'selected') if  $args->{value} eq '';
940             $a->push_content( $null ); 
941         }
942            
943     my ($opt0, $opt1) = ( HTML::Element->new("option", value => 0),
944                                                   HTML::Element->new("option", value => 1) ); 
945     $opt0->push_content($bool_text[0]); 
946     $opt1->push_content($bool_text[1]); 
947         unless ($selected eq '') { 
948         $opt0->attr("selected" => "selected") if not $selected; 
949         $opt1->attr("selected" => "selected") if $selected; 
950         }
951     $a->push_content($opt0, $opt1);
952     $OLD_STYLE && return $a->as_HTML;
953     $a;
954 }
955
956
957 =head2 _to_hidden($field, $args)
958
959 This makes a hidden html element input. It uses the "name" and "value" 
960 arguments. If one or both are not there, it will look for an object in 
961 "items->[0]" or the caller. Then it will use $field or the primary key for
962 name  and the value of the column by the derived name.
963
964 =cut
965
966 sub _to_hidden {
967     my ($self, $field, $args) = @_;
968     $args ||= {};
969         my ($name, $value) = ($args->{'name'}, $args->{value});
970         $name = $field unless defined $name;
971         if (! defined $name and !defined $value) { # check for objects
972         my $obj = $args->{items}->[0] || $self;
973                 unless (ref $obj) { die "_to_hidden cannot determine a value. It was passed a value argument or items object or called with an object."; }
974                 $name = $obj->primary_column->name unless $name;
975                 $value = $obj->$name unless $value;
976         }
977
978     return HTML::Element->new('input', 'type' => 'hidden',
979                               'name' => $name, 'value'=>$value);
980     
981 }
982
983 =head2 _to_link_hidden($col, $args) 
984
985 Makes a link with a hidden input with the id of $obj as the value and name.
986 Name defaults to the objects primary key. The object defaults to self.
987
988 =cut
989
990 sub _to_link_hidden {
991     my ($self, $accessor, $args) = @_;
992     my $r =  eval {$self->controller} || $args->{r} || '';
993     my $uri = $args->{uri} || '';
994    use Data::Dumper;
995     $self->_croak("_to_link_hidden cant get uri. No  Maypole Request class (\$r) or uri arg. Need one or other.")
996         unless $r;
997     my ($obj, $name);
998     if (ref $self) { # hidding linking self
999          $obj  = $self;
1000          $name = $args->{name} || $obj->primary_column->name;
1001     }
1002     elsif ($obj = $args->{items}->[0]) {
1003         $name = $args->{name} || $accessor || $obj->primary_column->name; 
1004                 # TODO use meta data above maybe
1005     }
1006     else {           # hiding linking related object with id in args
1007         $obj  = $self->related_class($r, $accessor)->retrieve($args->{id});
1008         $name = $args->{name} || $accessor ; #$obj->primary_column->name;
1009                 # TODO use meta data above maybe
1010     }
1011     $self->_croak("_to_link_hidden has no object") unless ref $obj;
1012     my $href =  $uri || $r->config->{uri_base} . "/". $obj->table."/view/".$obj->id;
1013     my $a = HTML::Element->new('a', 'href' => $href);
1014     $a->push_content("$obj");
1015     $a->push_content($self->to_field('blahfooey', 'hidden', {name => $name, value =>  $obj->id} ));
1016
1017         $OLD_STYLE && return $a->as_HTML;
1018     $a;
1019 }
1020
1021 =head2 _to_foreign_inputs
1022
1023 Creates inputs for a foreign class, usually related to the calling class or 
1024 object. In names them so they do not clash with other names and so they 
1025 can be processed generically.  See _rename_foreign_inputs below  and 
1026 Maypole::Model::CDBI::FromCGI::classify_foreign_inputs.
1027
1028 Arguments this recognizes are :
1029
1030         related_meta -- if you have this, great, othervise it will determine or die
1031         columns  -- list of columns to make inputs for 
1032         request (r) -- TODO the Maypole request so we can see what action  
1033
1034 =cut
1035
1036 sub _to_foreign_inputs {
1037         my ($self, $accssr, $args) = @_;
1038         my $rel_meta = $args->{related_meta} || $self->related_meta('r',$accssr); 
1039         my $fields              = $args->{columns};
1040         if (!$rel_meta) {
1041                 $self->_croak( "No relationship for accessor $accssr");
1042         }
1043
1044         my $rel_type = $rel_meta->{name};
1045         my $classORobj = ref $self && ref $self->$accssr ? $self->$accssr : $rel_meta->{foreign_class};
1046         
1047         unless ($fields) {      
1048                 $fields = $classORobj->can('display_columns') ? 
1049                         [$classORobj->display_columns] : [$classORobj->columns];
1050         }
1051         
1052         # Ignore our fkey in them to  prevent infinite recursion 
1053         my $me          = eval {$rel_meta->{args}{foreign_key}} || 
1054                                           eval {$rel_meta->{args}{foreign_column}}
1055                           || ''; # what uses foreign_column has_many or might_have  
1056         my $constrained = $rel_meta->{args}{constraint}; 
1057         my %inputs;
1058         foreach ( @$fields ) {
1059                 next if $constrained->{$_} || ($_ eq $me); # don't display constrained
1060                 $inputs{$_} =  $classORobj->to_field($_);
1061         }
1062
1063         # Make hidden inputs for constrained columns unless we are editing object
1064         # TODO -- is this right thing to do?
1065         unless (ref $classORobj || $args->{no_hidden_constraints}) {
1066                 $inputs{$_} = $classORobj->to_field('blahfooey', 'hidden', 
1067                                       {name => $_, value => $constrained->{$_}} ) 
1068                         foreach ( keys %$constrained );  
1069         }
1070         $self->_rename_foreign_input($accssr, \%inputs);
1071         return \%inputs;
1072 }
1073
1074
1075 =head2 _hash_selected
1076
1077 *Function* to make sense out of the "selected" argument which has values of the 
1078 options that should be selected by default when making a select box.  It
1079 can be in a number formats.  This method returns a map of which options to 
1080 select with the values being the keys in the map ( {val1 => 1, val2 = 1} ).
1081
1082 Currently this method  handles the following formats for the "selected" argument
1083 and in the following ways
1084
1085   Object                                -- uses the id method  to get the value
1086   Scalar                                -- assumes it *is* the value
1087   Array ref of objects  -- same as Object
1088   Arrays of data                -- uses the 0th element in each
1089   Hashes of data                -- uses key named 'id'
1090     
1091 =cut 
1092  
1093 ############
1094 # FUNCTION #
1095 ############
1096 sub _hash_selected {
1097         my ($args) = shift;
1098         my $selected = $args->{value} || $args->{selected};
1099         #warn "**** SELECTED is $selected ****";
1100         my $type = ref $selected;
1101     return $selected unless $selected and $type ne 'HASH'; 
1102         #warn "Selected dump : " . Dumper($selected);
1103         # Single Object 
1104     if ($type and $type ne 'ARRAY') {
1105            my $id = $selected->id;
1106            $id =~ s/^0*//;
1107        return  {$id => 1};
1108     }
1109     # Single Scalar id 
1110         elsif (not $type) {
1111                 return { $selected => 1}; 
1112         }
1113         
1114
1115         # Array of objs, arrays, hashes, or just scalalrs. 
1116         elsif ($type eq 'ARRAY') {
1117                 my %hashed;
1118                 my $ltype = ref $selected->[0];
1119                 # Objects
1120                 if ($ltype and $ltype ne 'ARRAY')  {
1121                         %hashed = map { $_->id  => 1 } @$selected;
1122         }
1123                 # Arrays of data with id first 
1124             elsif ($ltype and $ltype eq 'ARRAY') {
1125                         %hashed = map { $_->[0]  => 1 } @$selected; 
1126                 }
1127                 # Hashes using pk or id key
1128                 elsif ($ltype and $ltype eq 'HASH') {
1129                         my $pk = $args->{class}->primary_column || 'id';
1130                         %hashed = map { $_->{$pk}  => 1 } @$selected; 
1131                 }
1132                 # Just Scalars
1133         else { 
1134                         %hashed = map { $_  => 1 } @$selected; 
1135                 }
1136                 return \%hashed;
1137         }
1138         else { warn "AsForm Could not hash the selected argument: $selected"; }
1139
1140                 
1141
1142
1143
1144 =head2 _select_guts 
1145
1146 Internal api  method to make the actual select box form elements. 
1147 the data.
1148
1149 Items to make options out of can be 
1150   Hash, Array, 
1151   Array of CDBI objects.
1152   Array of scalars , 
1153   Array or  Array refs with cols from class,
1154   Array of hashes 
1155
1156 =cut
1157
1158
1159
1160 sub _select_guts {
1161     my ($self, $col, $args) = @_; #$nullable, $selected_id, $values) = @_;
1162
1163     #$args->{stringify} ||=  'stringify_selectbox';
1164
1165     $args->{selected} = _hash_selected($args) if defined $args->{selected};
1166         my $name = $args->{name} || $col;
1167     my $a = HTML::Element->new('select', name => $name);
1168         $a->attr( %{$args->{attr}} ) if $args->{attr};
1169     
1170     if ($args->{column_nullable}) {
1171                 my $null_element = HTML::Element->new('option', value => '');
1172         $null_element->attr(selected => 'selected')
1173                 if ($args->{selected}{'null'});
1174         $a->push_content($null_element);
1175     }
1176
1177         my $items = $args->{items};
1178     my $type = ref $items;
1179         my $proto = eval { ref $items->[0]; } || "";
1180         my $optgroups = $args->{optgroups} || '';
1181         
1182         # Array of hashes, one for each optgroup
1183         if ($optgroups) {
1184                 my $i = 0;
1185                 foreach (@$optgroups) {
1186                         my $ogrp=  HTML::Element->new('optgroup', label => $_);
1187                         $ogrp->push_content($self->_options_from_hash($items->[$i], $args));
1188                         $a->push_content($ogrp);
1189                         $i++;
1190                 }
1191         }               
1192     # Single Hash
1193     elsif ($type eq 'HASH') {
1194         $a->push_content($self->_options_from_hash($items, $args));
1195     }
1196     # Single Array
1197     elsif ( $type eq 'ARRAY' and not ref $items->[0] ) {
1198         $a->push_content($self->_options_from_array($items, $args));
1199     }
1200     # Array of Objects
1201     elsif( $type eq 'ARRAY' and $proto !~ /ARRAY|HASH/i ) {
1202         # make select  of objects
1203         $a->push_content($self->_options_from_objects($items, $args));
1204     }
1205     # Array of Arrays
1206     elsif ( $type eq 'ARRAY' and $proto eq 'ARRAY' ) {
1207         $a->push_content($self->_options_from_arrays($items, $args));
1208     }
1209     # Array of Hashes
1210     elsif ( $type eq 'ARRAY' and $proto eq 'HASH' ) {
1211         $a->push_content($self->_options_from_hashes($items, $args));
1212     }
1213     else {
1214         die "You passed a weird type of data structure to me. Here it is: " .
1215         Dumper($items );
1216     }
1217
1218     return $a;
1219
1220
1221 }
1222
1223 =head2 _options_from_objects ( $objects, $args);
1224
1225 Private method to makes a options out of  objects. It attempts to call each
1226 objects stringify method specified in $args->{stringify} as the content. Otherwise the default stringification prevails.
1227
1228 *Note only  single primary keys supported
1229
1230 =cut
1231 sub _options_from_objects {
1232     my ($self, $items, $args) = @_;
1233         my $selected = $args->{selected} || {};
1234         my $stringify = $args->{stringify} || '';
1235     my @res;
1236         for (@$items) {
1237                 my $id = $_->id;
1238                 my $opt = HTML::Element->new("option", value => $id);
1239                 $id =~ s/^0*//; # leading zeros no good in hash key
1240                 $opt->attr(selected => "selected") if $selected->{$id}; 
1241                 my $content = $stringify ? $_->stringify :  "$_";
1242                 $opt->push_content($content);
1243                 push @res, $opt; 
1244         }
1245     return @res;
1246 }
1247
1248 sub _options_from_arrays {
1249     my ($self, $items, $args) = @_;
1250         my $selected = $args->{selected} || {};
1251     my @res;
1252         my $class = $args->{class} || '';
1253         my $stringify = $args->{stringify} || '';
1254         for my $item (@$items) {
1255             my @pks; # for future multiple key support
1256             push @pks, shift @$item foreach $class->columns('Primary');
1257                 my $id = $pks[0];
1258                 $id =~ s/^0+//;  # In case zerofill is on .
1259                 my $val = defined $id ? $id : '';
1260                 my $opt = HTML::Element->new("option", value =>$val);
1261                 $opt->attr(selected => "selected") if $selected->{$id};
1262                 
1263                 my $content = ($class and $stringify and $class->can($stringify)) ? 
1264                               $class->$stringify($_) : 
1265                                   join( '/', map { $_ if $_; }@{$item} );
1266                 $opt->push_content( $content );
1267         push @res, $opt; 
1268     }
1269     return @res;
1270 }
1271
1272
1273 sub _options_from_array {
1274     my ($self, $items, $args) = @_;
1275     my $selected = $args->{selected} || {};
1276     my @res;
1277     for (@$items) {
1278                 my $val = defined $_ ? $_ : '';
1279         my $opt = HTML::Element->new("option", value => $val);
1280         #$opt->attr(selected => "selected") if $selected =~/^$id$/;
1281         $opt->attr(selected => "selected") if $selected->{$_};
1282         $opt->push_content( $_ );
1283         push @res, $opt;
1284     }
1285     return @res;
1286 }
1287
1288 sub _options_from_hash {
1289     my ($self, $items, $args) = @_;
1290     my $selected = $args->{selected} || {};
1291     my @res;
1292
1293     my @values = values %$items;
1294     # hash Key is the option content  and the hash value is option value
1295     for (sort keys %$items) {
1296                 my $val = defined $items->{$_} ? $items->{$_} : '';
1297         my $opt = HTML::Element->new("option", value => $val);
1298         #$opt->attr(selected => "selected") if $selected =~/^$id$/;
1299         $opt->attr(selected => "selected") if $selected->{$items->{$_}};
1300         $opt->push_content( $_ );
1301         push @res, $opt;
1302     }
1303     return @res;
1304 }
1305
1306
1307 sub _options_from_hashes {
1308     my ($self, $items, $args) = @_;
1309         my $selected = $args->{selected} || {};
1310         my $pk = eval {$args->{class}->primary_column} || 'id';
1311         my $fclass = $args->{class} || '';
1312         my $stringify = $args->{stringify} || '';
1313         my @res;
1314         for my $item (@$items) {
1315                 my $val = defined $item->{$pk} ? $item->{$pk} : '';
1316                 my $opt = HTML::Element->new("option", value => $val);
1317                 $opt->attr(selected => "selected") if $selected->{$val};
1318                 my $content = ($fclass and $stringify and $fclass->can($stringify)) ? 
1319                               $fclass->$stringify($_) : 
1320                                   join(' ', map {$item->{$_} } keys %$item);
1321                 $opt->push_content( $content );
1322         push @res, $opt; 
1323     }
1324         return @res;
1325 }
1326
1327 # TODO -- Maybe
1328 #sub _to_select_or_create {
1329 #       my ($self, $col, $args) = @_;
1330 #       $args->{name} ||= $col;
1331 #       my $select = $self->to_field($col, 'select', $args);
1332 #       $args->{name} = "create_" . $args->{name};
1333 #       my $create = $self->to_field($col, 'foreign_inputs', $args);
1334 #       $create->{'__select_or_create__'} = 
1335 #               $self->to_field('__select_or_create__',{ name => '__select_or_create__' , value => 1 } );
1336 #       return ($select, $create);
1337 #}
1338         
1339
1340 =head2 _to_checkbox 
1341
1342 Makes a checkbox element -- TODO
1343
1344 =cut
1345
1346 # checkboxes: if no data in hand (ie called as class method), replace
1347 # with a radio button, in order to allow this field to be left
1348 # unspecified in search / add forms.
1349
1350 # Not tested
1351 # TODO  --  make this general checkboxse
1352
1353 #
1354 sub _to_checkbox {
1355     my ($self, $col, $args) = @_;
1356     my $nullable = eval {self->column_nullable($col)} || 0; 
1357     return $self->_to_radio($col) if !ref($self) || $nullable;
1358     my $value = $self->$col;
1359     my $a = HTML::Element->new("input", type=> "checkbox", name => $col);
1360     $a->attr("checked" => 'true') if $value eq 'Y';
1361     return $a;
1362 }
1363
1364 =head2 _to_radio
1365
1366 Makes a radio button element -- TODO
1367
1368 =cut
1369 # TODO  -- make this general radio butons
1370 #
1371 sub _to_radio {
1372     my ($self, $col) = @_;
1373     my $value = ref $self && $self->$col || '';
1374     my $nullable = eval {self->column_nullable($col)} || 0; 
1375     my $a = HTML::Element->new("span");
1376     my $ry = HTML::Element->new("input", type=> "radio", name=>$col, value=>'Y' );
1377     my $rn = HTML::Element->new("input", type=> "radio", name=>$col, value=>'N' );
1378     my $ru = HTML::Element->new("input", type=> "radio", name=>$col, value=>'' ) if $nullable;
1379     $ry->push_content('Yes'); $rn->push_content('No');
1380     $ru->push_content('n/a') if $nullable;
1381     if ($value eq 'Y') { $ry->attr("checked" => 'true') }
1382     elsif ($value eq 'N') { $rn->attr("checked" => 'true') }
1383     elsif ($nullable) { $ru->attr("checked" => 'true') }
1384     $a->push_content($ry, $rn);
1385     $a->push_content($ru) if $nullable;
1386     return $a;
1387 }
1388
1389
1390
1391 ############################ HELPER METHODS ######################
1392 ##################################################################
1393
1394 =head2 _rename_foreign_input
1395
1396 _rename_foreign_input($html_el_or_hash_of_them); # changes made by reference
1397
1398 Recursively renames the foreign inputs made by _to_foreign_inputs so they 
1399 can be processed generically.  It uses foreign_input_delimiter. 
1400
1401 So if an Employee is a Person who has_many  Addresses and you call and the
1402 method 'foreign_input_delimiter' returns '__AF__' then 
1403
1404   Employee->to_field("person");  
1405   
1406 will get inputs for the Person as well as their Address (by default,
1407 override _field_from_relationship to change logic) named like this: 
1408
1409   person__AF__address__AF__street
1410   person__AF__address__AF__city
1411   person__AF__address__AF__state  
1412   person__AF__address__AF__zip  
1413
1414 And the processor would know to create this address, put the address id in
1415 person->{address} data slot, insert the person and put the person id in the employee->{person} data slot and then insert the employee with that data.
1416
1417 =cut
1418
1419 sub _rename_foreign_input {
1420         my ($self, $accssr, $element) = @_;
1421         my $del = $self->foreign_input_delimiter;
1422         
1423         if ( ref $element ne 'HASH' ) {
1424         #       my $new_name = $accssr . "__AF__" . $input->attr('name');
1425                 $element->attr( name => $accssr . $del . $element->attr('name'));
1426         }
1427         else {
1428                 $self->_rename_foreign_input($accssr, $element->{$_}) 
1429                         foreach (keys %$element);
1430         }
1431 }
1432
1433 =head2 foreign_input_delimiter
1434
1435 This tells AsForm what to use to delmit forieign input names. This is important
1436 to avoid name clashes as well as automating processing of forms. 
1437
1438 =cut
1439
1440 sub foreign_input_delimiter { '__AF__' };
1441
1442 =head2 _box($value) 
1443
1444 This functions computes the dimensions of a textarea based on the value 
1445 or the defaults.
1446
1447 =cut
1448
1449 sub _box
1450 {
1451         
1452         my ($min_rows, $max_rows, $min_cols, $max_cols) = (2 => 50, 20 => 100);
1453     my $text = shift;
1454     if ($text) {
1455         my @rows = split /^/, $text;
1456         my $cols = $min_cols;
1457         my $chars = 0;
1458         for (@rows) {
1459             my $len = length $_;
1460             $chars += $len;
1461             $cols = $len if $len > $cols;
1462             $cols = $max_cols if $cols > $max_cols;
1463         }
1464         my $rows = @rows;
1465         $rows = int($chars/$cols) + 1 if $chars/$cols > $rows;
1466         $rows = $min_rows if $rows < $min_rows;
1467         $rows = $max_rows if $rows > $max_rows;
1468         ($rows, $cols)
1469     }
1470     else { ($min_rows, $min_cols) }
1471 }
1472
1473
1474 1;
1475
1476
1477 =head1 CHANGES
1478
1479 1.0 
1480 15-07-2004 -- Initial version
1481 =head1 MAINTAINER 
1482
1483 Maypole Developers
1484
1485 =head1 AUTHORS
1486
1487 Peter Speltz, Aaron Trevena 
1488
1489 =head1 AUTHORS EMERITUS
1490
1491 Simon Cozens, Tony Bowden
1492
1493 =head1 TODO
1494
1495   Documenting 
1496   Testing - lots
1497   chekbox generalization
1498   radio generalization
1499   select work
1500   Make link_hidden use standard make_url stuff when it gets in Maypole
1501   How do you tell AF --" I want a has_many select box for this every time so,
1502      when you call "to_field($this_hasmany)" you get a select box
1503
1504 =head1 BUGS and QUERIES
1505
1506 Please direct all correspondence regarding this module to:
1507  Maypole list. 
1508
1509 =head1 COPYRIGHT AND LICENSE
1510
1511 Copyright 2003-2004 by Simon Cozens / Tony Bowden
1512
1513 This library is free software; you can redistribute it and/or modify
1514 it under the same terms as Perl itself.
1515
1516 =head1 SEE ALSO
1517
1518 L<Class::DBI>, L<Class::DBI::FromCGI>, L<HTML::Element>.
1519
1520 =cut
1521