--- /dev/null
+package Maypole::Model::CDBI::AsForm;
+
+use 5.006;
+
+use strict;
+use warnings;
+
+use base 'Exporter';
+use Data::Dumper;
+use Class::DBI::Plugin::Type ();
+use HTML::Element;
+
+our $OLD_STYLE = 0;
+# pjs -- Added new methods to @EXPORT
+our @EXPORT = qw( to_cgi to_field _to_textarea _to_textfield _to_select
+ type_of _to_foreign_inputs _to_enum_select _to_bool_select
+ to_select_from_many _to_select_from_related hasmany_class
+ _to_hidden _rename_foreign_input _to_readonly
+ make_param_foreign make_hidden_elmnt make_hidden_elmnt
+ a_select_box unselect_element do_select search_inputs);
+
+
+
+our $VERSION = '2.41';
+# PJS VERSION .05
+# Changes :
+# 08-09-05 - fixed broken has_a select box
+# - fiked some docs
+# - _to_foreign_inputs now takes 3 positional parameters
+# (accssr, fields, accssr_meta_info)
+
+
+=head1 NAME
+
+Maypole::Model:CDBI::AsForm - Produce HTML form elements for database columns
+
+=head1 SYNOPSIS
+
+ package Music::CD;
+ use Maypole::Model::CDBI::AsForm;
+ use base 'Class::DBI';
+ use CGI;
+ ...
+
+ sub create_or_edit {
+ my $class = shift;
+ my %cgi_field = $class->to_cgi;
+ return start_form,
+ (map { "<b>$_</b>: ". $cgi_field{$_}->as_HTML." <br>" }
+ $class->Columns),
+ end_form;
+ }
+
+ # <form method="post"...>
+ # Title: <input type="text" name="Title" /> <br>
+ # Artist: <select name="Artist">
+ # <option value=1>Grateful Dead</option>
+ # ...
+ # </select>
+ # ...
+ # </form>
+
+=head1 DESCRIPTION
+
+This module helps to generate HTML forms for creating new database rows
+or editing existing rows. It maps column names in a database table to
+HTML form elements which fit the schema. Large text fields are turned
+into textareas, and fields with a has-a relationship to other
+C<Class::DBI> tables are turned into select drop-downs populated with
+objects from the joined class.
+
+=head1 METHODS
+
+The module is a mix-in which adds two additional methods to your
+C<Class::DBI>-derived class.
+
+
+=head2 search_inputs
+
+Returns hashref of search inputs elements to use in cgi.
+
+Uses fields specified in search_fields, makes foreign inputs if necessary.
+
+=cut
+
+sub search_inputs {
+ my ($class, $r) = @_;
+ warn "In model search_inputs " if $class->model_debug;
+ $class = ref $class || $class;
+ #my $accssr_class = { $class->accessor_classes };
+ my %cgi;
+ my $sfs = $class->search_fields;
+
+ foreach my $field ( @$sfs ) {
+ if ( ref $field eq "HASH" ) { # foreign search fields
+ my ($accssr, $cols) = each %$field;
+ unless ( @$cols ) {
+ # default to search fields for related
+ #$cols = $accssr_class->{$accssr}->search_fields;
+ die ("$class search_fields error: Must specify at least one column to search in the foreign object named '$accssr'");
+ }
+ my $fcgi = $class->_to_foreign_inputs($accssr, $cols);
+ # unset the default values for a select box
+ foreach (keys %$fcgi) {
+ $class->unselect_element($fcgi->{$_});
+ }
+ $cgi{$accssr} = $fcgi;
+ #warn "Searchy inputs for field $field is " . Dumper($cgi{$accssr});
+ } else {
+ $cgi{$field} = $class->to_field($field);
+ $class->unselect_element($cgi{$field});
+ }
+ }
+ return \%cgi;
+}
+
+
+=head2 do_select
+
+Retrieves object selected from a select box and puts in $r->objects[0].
+The select box input must be named the same as the primary key.
+
+NOTE only works with tables with single primary key for now.
+
+=cut
+
+sub do_select {
+ my ($self, $r) = @_;
+ $r->objects([ $self->retrieve($r->params->{$self->primary_column}) ]);
+ $r->template('view');
+}
+
+
+=head2 unselect_element
+
+Unselects all options in a HTML::Element of type select.
+It does nothing if element is not a select element.
+
+=cut
+
+sub unselect_element {
+ my ($self, $el) = @_;
+ #unless (ref $el eq 'HTML::Element') {
+ #$self->_croak ('Need an HTML::Element to unselect. You gave a ' . Dumper($el)); }
+ if ($el->tag eq 'select') {
+ foreach my $opt ($el->content_list) {
+ $opt->attr('selected', undef) if $opt->attr('selected');
+ }
+ }
+}
+
+
+# make a select box from args
+sub a_select_box {
+ my ($self, $name, $vals, $selected_val, $contents) = @_;
+ die "Usage: Need a name and array ref of values to make a select boxes" unless ($name && $vals);
+ $selected_val ||= "";
+ $contents ||= $vals ;
+
+ my $a = HTML::Element->new('select', 'name' => $name);
+ my $i = 0;
+ my $c;
+ foreach my $v ( @$vals ) {
+ my $opt = HTML::Element->new('option', 'value' => $v);
+ $opt->attr('selected' => 'selected') if $v eq $selected_val;
+ $c = $contents->[$i++] || $v;
+ $opt->push_content($c);
+ $a->push_content($opt);
+ }
+ $a;
+}
+
+
+=head2 make_hidden_elmnt
+
+Makes a hidden HTML::Element and puts it in template_args{hidden_elements}
+$model->make_hidden_elmnt($name, $val);
+
+=cut
+
+sub make_hidden_elmnt {
+ my ($self, $r, $col, $val) = @_;
+ my $elmnt = HTML::Element->new('input', 'type'=>'hidden', 'name'=>$col, 'value'=>$val);
+
+ $r->{template_args}{hidden_elements} ||= [];
+ push @{ $r->{template_args}{hidden_elements} }, $elmnt;
+}
+
+
+
+=head2 make_param_foreign
+
+Makes a new foreign parameter out of parameter and accessor
+Just puts accssr__FOREIGN__ in front of param name
+
+=cut
+
+sub make_param_foreign {
+ my ($self, $r, $p, $accssr) = @_;
+ $r->params->{$accssr . '__FOREIGN__' . $p} = $r->params->{$p};
+}
+
+=head2 to_cgi
+
+This returns a hash mapping all the column names of the class to
+HTML::Element objects representing form widgets.
+
+pjs -- added a columns list argument to specify which columns to make
+inputs for.
+
+=cut
+
+sub to_cgi {
+ my ($class, @columns) = @_; # pjs -- added columns arg
+ @columns = $class->columns unless (@columns);
+ map { $_ => $class->to_field($_) } @columns;
+}
+
+
+=head2 to_field($field [, $how])
+
+This maps an individual column to a form element. The C<how> argument
+can be used to force the field type into one of C<textfield>, C<textarea>
+or C<select>; you can use this is you want to avoid the automatic detection
+of has-a relationships.
+
+# pjs
+ -- added support for enum and bool. Note for enum and bool you need
+ a better column_type method than the Plugin::Type ' s as it won't work
+ if you are using MySQL. I have not tried others.
+ See those method's docs below.
+ -- Foreign inputs for might_have, must_have, has_own, and has_many(i think).
+ -- Really any relationship except has_a and is_a as has_a gets a select box
+ and is_a are not considered foreign.
+ -- Note a good column_type sub can be
+ used to get the correct type for is_a columns.
+ -- More efficient _to_select -- no object creation.
+ -- Attempts to set default value in field for you using a "column_default"
+ method you write yourself or your CDBI driver like mysql writes.
+ -- _to_hidden
+
+=cut
+
+sub to_field {
+ my ($self, $field, $how) = @_;
+ my $class = ref $self || $self;
+ if ($how and $how =~ /^(text(area|field)|select)$/) {
+ no strict 'refs';
+ my $meth = "_to_$how";
+ return $self->$meth($field);
+ }
+
+ my $meta = $self->meta_info;
+ my ($rel_type) = grep{ defined $meta->{$_}{$field} } keys %$meta;
+ $rel_type ||= '';
+ my $fclass = $rel_type ? $meta->{$rel_type}{$field}{foreign_class} : '';
+ my $fclass_is_cdbi = $fclass ? $fclass->isa('Class::DBI') : 0;
+
+ # maybe has_a select
+ return $self->_to_select($field, $fclass) if $rel_type eq 'has_a' and
+ $fclass_is_cdbi;
+
+ # maybe foreign inputs
+ my %local_cols = map { $_ => 1 } $self->columns; # includes is_a cols
+ if ($fclass_is_cdbi and (not $local_cols{$field} or $rel_type eq 'has_own'))
+ {
+ return $self->_to_foreign_inputs($field, undef, $meta->{$rel_type}{$field});
+ }
+
+ # the rest
+ my $type;
+ if ($class->can('column_type')) {
+ $type = $class->column_type($field);
+ }
+ else {
+ # Right, have some of this
+ eval "package $class; Class::DBI::Plugin::Type->import()";
+ $type = $class->column_type($field);
+ }
+
+ #return $self->_to_textfield($field)
+ # if $type and $type =~ /(var)?char/i; #common type
+ return $self->_to_textarea($field)
+ if $type and $type =~ /^(TEXT|BLOB)$/i;
+ return $self->_to_enum_select($field, $type)
+ if $type and $type =~ /^ENUM\((.*?)\)$/i;
+ return $self->_to_bool_select($field, $type)
+ if $type and $type =~ /^BOOL/i;
+ return $self->_to_readonly($field)
+ if $type and $type =~ /^readonly$/i;
+ return $self->_to_textfield($field);
+}
+
+sub _to_textarea {
+ my ($self, $col) = @_;
+ # pjs added default
+ my $a =
+ HTML::Element->new("textarea", name => $col, rows => "3", cols => "22");
+ my $val;
+ if (ref $self) {
+ $val = $self->$col;
+ }
+ else {
+ $val = eval {$self->column_default($col);};
+ $val = '' unless defined $val;
+ }
+ $a->push_content($val);
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+sub _to_textfield {
+ my ($self, $col) = @_;
+ # pjs added default
+ my $val;
+ if (ref $self) {
+ $val = $self->$col;
+ }
+ else {
+ $val = eval {$self->column_default($col);};
+ $val = '' unless defined $val;
+ }
+
+ my $a = HTML::Element->new("input", type => "text", name => $col);
+ $a->attr("value" => $val);
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+# pjs
+# -- Rewrote this to be efficient -- no object creation.
+# -- Added option for CDBI classes to specify a limiting clause
+# via "has_a_select_limit".
+# -- Added selected argument to set a selected
+
+sub _to_select {
+ my ($self, $col, $hint, $selected) = @_;
+ my $has_a_class;
+ if (not $col) { # class is making select box of self
+ $has_a_class = ref $self || $self;
+ $col = $self->primary_column;
+ }
+ else {
+ $has_a_class = $hint || $self->__hasa_rels->{$col}->[0];
+ }
+
+ $selected ||= {};
+ if (ref $self and my $id = eval { $self->$col->id }) {
+ $selected->{$id} = 1;
+ }
+ #pjs Possible sql limiting clause ("WHERE acol = 'X' " or "LIMIT 10", etc)
+ my $select_box_limit = eval { $self->has_a_select_limit->{$col} } || '' ;
+
+ # Get columns to appear in select box options on forms.
+ # TODO -- there is maybe a good idiom for this.
+ my @select_box_cols;
+ @select_box_cols = $has_a_class->columns('SelectBox');
+ @select_box_cols = $has_a_class->columns('Stringify')
+ unless @select_box_cols;
+ @select_box_cols = $has_a_class->_essential
+ unless @select_box_cols;
+ unshift @select_box_cols, $has_a_class->columns('Primary');
+ my $sql = "SELECT " . join( ', ', @select_box_cols) . " FROM " .
+ $has_a_class->table . " " . $select_box_limit;
+ my $opts_data = $self->db_Main->selectall_arrayref($sql);
+
+ my $a = HTML::Element->new("select", name => $col);
+ for (@$opts_data) {
+ my $id = shift @$_;
+ my $opt = HTML::Element->new("option", value => $id );
+ $opt->attr("selected" => "selected") if $selected->{$id};
+ my $content = eval {$has_a_class->stringify_selectbox($_);} ||
+ join(' ', @$_);
+ $opt->push_content( $content );
+ $a->push_content($opt);
+ }
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+# Makes a readonly input box out of column's value
+# Currently object method only
+sub _to_readonly {
+ my ($self, $col, $val) = @_;
+ unless (defined $val) {
+ $self->_croak("Cannot call _to_readonly on class without value arg.")
+ unless ref $self;
+ $val = $self->$col;
+ }
+ my $a = HTML::Element->new('input', 'type' => 'text', readonly => '1',
+ 'name' => $col, 'value'=>$val);
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+=head2 _to_enum_select
+
+$sel_box = $self->_to_enum_select($column, "ENUM('Val1','Val2','Val3')");
+
+Returns an enum select box given a column name and an enum string.
+NOTE: The Plugin::Type does not return an enum string for mysql enum columns.
+This will not work unless you write your own column_type method in your model.
+
+=cut
+
+sub _to_enum_select {
+ my ($self, $col, $type) = @_;
+ $type =~ /ENUM\((.*?)\)/i;
+ (my $enum = $1) =~ s/'//g;
+ my @enum_vals = split /\s*,\s*/, $enum;
+
+ my $a = HTML::Element->new("select", name => $col);
+ for ( @enum_vals ) {
+ my $sel = HTML::Element->new("option", value => $_);
+ $sel->attr("selected" => "selected") if ref $self
+ and eval { $self->$col eq $_ };
+ $sel->push_content($_);
+ $a->push_content($sel);
+ }
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+
+=head2 _to_bool_select
+
+ my $sel = $self->_to_bool_select($column, $bool_string);
+
+This makes select input for boolean column. You can provide a
+bool string of form: Bool('zero','one') and those are used for option
+content. Onthervise No and Yes are used.
+TODO -- test without bool string.
+
+=cut
+
+sub _to_bool_select {
+ my ($self, $col, $type) = @_;
+ my @bool_text = ('No', 'Yes');
+ if ($type =~ /BOOL\((.+?)\)/i) {
+ (my $bool = $1) =~ s/'//g;
+ @bool_text = split /,/, $bool;
+ }
+ my $one= ref $self ? eval {$self->$col;} : $self->column_default($col);
+ my $a = HTML::Element->new("select", name => $col);
+ my ($opt0, $opt1) = ( HTML::Element->new("option", value => 0),
+ HTML::Element->new("option", value => 1) );
+ $opt0->attr("selected" => "selected") if not $one;
+ $opt0->push_content($bool_text[0]);
+ $opt1->attr("selected" => "selected") if $one;
+ $opt1->push_content($bool_text[1]);
+ $a->push_content($opt0, $opt1);
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+
+=head2 _to_hidden($name, $value)
+
+This makes a hidden html element. Give it a name and value.
+
+=cut
+sub _to_hidden {
+ my ($self, $name, $val) = @_;
+ return HTML::Element->new('input', 'type' => 'hidden',
+ 'name' => $name, 'value'=>$val
+ );
+}
+
+
+
+=head2 _to_foreign_inputs
+
+$html_els = $class_or_obj->_to_foreign_inputs($accssr, [$fields, $accssr_meta]);
+
+Get inputs for the accessor's class. Pass an array ref of fields to get
+inputs for only those fields. Otherwise display_columns or all columns is used.
+If you have the meta info handy for the accessor you can pass that too.
+
+TODO make AsForm know more about the request like what action we are doing
+so it can use edit columns or search_columns
+
+NOTE , this names the foreign inputs is a particular way so they can be
+processed with a general routine and so there are not name clashes.
+
+=cut
+
+sub _to_foreign_inputs {
+ my ($self, $accssr, $fields, $accssr_meta) = @_;
+ if (!$accssr_meta) {
+ my $class_meta = $self->meta_info;
+ my ($rel_type) = grep { defined $class_meta->{$_}->{$accssr} }
+ keys %$class_meta;
+ $self->_croak( "No relationship for accessor $accssr") if not $rel_type;
+ $accssr_meta = $class_meta->{$rel_type}->{$accssr};
+ }
+
+ my $classORobj = ref $self && ref $self->$accssr ? $self->$accssr : $accssr_meta->{foreign_class};
+
+ unless ($fields) {
+ $fields = $classORobj->can('display_columns') ?
+ [$classORobj->display_columns] : [$classORobj->columns];
+ }
+
+ # Ignore our fkey in them to prevent infinite recursion
+ my $me = eval {$accssr_meta->{args}{foreign_column}} || '';
+ my $constrained = $accssr_meta->{args}{constraint};
+ my %inputs;
+ foreach ( @$fields ) {
+ next if $constrained->{$_} || ($_ eq $me); # don't display constrained
+ $inputs{$_} = $classORobj->to_field($_);
+ }
+
+ # Make hidden inputs for constrained columns unless we are editing object
+ # TODO -- is this right thing to do?
+ unless (ref $classORobj) {
+ $inputs{$_} = $classORobj->_to_hidden($_, $constrained->{$_})
+ foreach ( keys %$constrained );
+ }
+ $self->_rename_foreign_input($accssr, \%inputs);
+ return \%inputs;
+}
+
+=head2 _rename_foreign_input
+
+_rename_foreign_input($html_el_or_hash_of_them); # changes made by reference
+
+Recursively renames the foreign inputs made by to_foreign_inputs so they
+can be processed generically. The format is "accessor__AsForeign_colname".
+
+So if an Employee is a Person who has own Address and you call
+
+ Employee->to_field("person")
+
+then you will get inputs for Address named like this:
+
+ person__AsForeign__address__AsForeign__street
+ person__AsForeign__address__AsForeign__city
+ person__AsForeign__address__AsForeign__state
+ person__AsForeign__address__AsForeign__zip
+
+And the processor would know to create this address, put the address id in
+person address slot, create the person and put the address id in the employee
+before creating the employee.
+
+=cut
+
+sub _rename_foreign_input {
+ my ($self, $accssr, $input) = @_;
+ if ( ref $input ne 'HASH' ) {
+ my $new_name = $accssr . "__AsForeign__" . $input->attr('name');
+ $input->attr( name => $new_name );
+ }
+ else {
+ $self->_rename_foreign_input($accssr, $input->{$_})
+ foreach (keys %$input);
+ }
+}
+
+
+# pjs
+
+=head2 to_select_from_many
+
+
+Usage: $object->to_select_from_many($accessor|$objects_to_select_from, [$element_name], [$options]);
+
+CD->has_many( 'songs' => "Songs" );
+... in some nearby piece of code:
+my $cd = CD->retrieve($id);
+my $select_song_html = $cd->to_select_from_many('songs');
+print "<h1>Choose your Favorite song from $cd</h1>";
+print $select_song_html.as_XML;
+return;
+
+# OR if you only want to select from a group of objects
+
+my @favorites = $cd->favorite_songs;
+my $select_from_favorites = $cd->to_select_from_many(\@favorites);
+
+This an object method that makes a select box out of the objects related to this object by a has_many relationship. The select box only allows one selection.
+The multiple attribute can easily be added if needed to the element returned :
+$this_element->attr('multiple', 'multiple');
+
+You can pass an array ref of objects to select from instead of the class accessor name if you already have the objects to select from.
+
+Also, you can pass the name you want the element to have as a second argument.
+The default is the primary key name (as returned by primary_column) of the firstobject that is being selected from.
+
+If related class has a multi column pk, this dies for now.#strange things may happen as primary_column method is used.
+
+=cut
+
+
+sub to_select_from_many {
+ my ($self, $accessor, $elmnt_name) = @_;
+ my $objs = ref $accessor eq "ARRAY" ? $accessor : [$self->$accessor];
+ my $rel_class = ( @$objs ) ? ref $objs->[0] :
+ eval{$self->hasmany_class($accessor)};
+
+ $elmnt_name = eval {$rel_class->primary_column} || "__AF_TSFM_OBJS__"
+ unless $elmnt_name;
+
+ return _to_select_from_objs($objs, $elmnt_name);
+
+}
+
+=head2 _to_select_from_objs($objects, $name, $selected);
+
+Private method to makes a select box of objects passed with name passed.
+Assumes they are same type
+
+=cut
+sub _to_select_from_objs {
+ my ($objs, $elmnt_name) = @_;
+ CGI::Carp::croak("Usage: element name required") unless ($elmnt_name);
+# $elmnt_name ||= eval {$objs->[0]->primary_column};
+# unless ($elmnt_name) {
+# my $num = @$objs;
+# $self->_carp ("Element name arg. not passed and couldn't get element name from object 0. Number of objects in arg are: $num");
+# return;
+# }
+
+ my $a = HTML::Element->new("select", name => $elmnt_name);
+ for (@$objs) {
+ my $opt = HTML::Element->new("option", value => $_->id);
+ $opt->push_content($_->stringify_self);
+ $a->push_content($opt);
+ }
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+
+# pjs EXPERIMENTAL
+# TODO this is crap. I think this will just be a public sub to select many objects from a class. Then you can do thingks like add them to has_many and stuff.
+#
+# not finished
+# usage: CD->has_many('songs', 'Song', 'cd_id');
+# my $song_sel_element = $class->_to_select_many('songs', @options);
+# @options have same form as a SQL::Abstract options with exception of
+# -HINT element which is the class name if you want to give it.
+# { '-HINT' => $classname, # so you can cheat, or be efficient
+# 'logic'=> 'OR', # default is OR
+# $limiting_col => $limit_val,
+# $limiting_col2=> $limit_val2,
+# . . . }
+#
+#
+# make select box for has many. This is a multiple select box (select many)
+# element. If you want to choose between on of the has_many's an object has (
+# ie -- a cd has many songs and you want to choose one of the songs from it)
+# then pass an additional hash ref of limiting cols and vals.
+# $cd->_to_many_select('songs', {'cd_id' => $cd->id, . . .}
+sub _to_select_many {
+ my ($self, $accessor, $hint, $where, $order ) = @_;
+ my $has_many_class = $hint || $self->hasmany_class($accessor);
+ my %selected = ();
+ %selected = map { $_->id => 1} $self->$accessor if ref $self;
+
+ my $pk = $has_many_class->primary_column;
+ my $a = $self->_to_select($pk, $has_many_class, \%selected, $where, $order);
+ $a->attr('multiple', 'multiple');
+
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+
+
+
+sub _to_select_old_version {
+ my ($self, $col, $hint) = @_;
+ my $has_a_class = $hint || $self->__hasa_rels->{$col}->[0];
+ my @objs = $has_a_class->retrieve_all;
+ my $a = HTML::Element->new("select", name => $col);
+ for (@objs) {
+ my $sel = HTML::Element->new("option", value => $_->id);
+ $sel->attr("selected" => "selected")
+ if ref $self
+ and eval { $_->id eq $self->$col->id };
+ $sel->push_content($_->stringify_self);
+ $a->push_content($sel);
+ }
+ $OLD_STYLE && return $a->as_HTML;
+ $a;
+}
+
+
+
+############################ HELPER METHODS ######################
+##################################################################
+
+# hasmany_class($accessor) -- stole code from Maypole::Model::CDBI
+# Returns class of has_many relationship when given the accessor
+sub hasmany_class {
+ my ( $self, $accessor ) = @_;
+ $self->_croak("No accessor (2nd arg) passed to hasmany_class")
+ unless $accessor;
+ my $rel_meta = $self->meta_info('has_many' => $accessor);
+
+ my $mapping;
+ if ( $mapping = $rel_meta->{args}->{mapping} and @$mapping ) {
+ return $rel_meta->{foreign_class}->meta_info('has_a')->{ $$mapping[0] }->{foreign_class};
+ }
+ else {
+ return $rel_meta->{foreign_class};
+ }
+}
+
+
+1;
+
+=head1 ***NEW PJS Stuff**** GETTING SPECIFIC IN MODEL CLASS
+
+You can tell AsForm some things in your model classes to get custom results. In particular you can have:
+
+=head2 Custom column_type methods
+
+Since much of this modules functionality relies on the subroutine C<column_type>
+returning the type string from the database table definition Model classes can
+benefit a great deal by writing their own. See example. This version tries to
+call column_type with the model class first. IF your model's column_type returns
+undef or it has no such method it falls back on
+C<&Class::DBI::Plugin::Type::column_type> which is database independent but not
+fully functional yet. For full functionality make a custom C<column_type> method
+in your base model class and override it in subclasses at will. Some \
+Class::DBI::* drivers such as Class::DBI::mysql have mostly functional ones.
+
+With a column_type sub you can set bool options for users , make select boxes
+for ordinary columns (by lying and returning an enum('blah', 'blh') string for a
+column, get correct types for is_a inherited columns, optimize , and maybe more.
+
+=head2 Appropriate elements for columns inherited from an is_a relationship
+
+At least you have the power to get them by making column_type work.
+
+=head2 Select box specifications for has_a columns.
+
+You can specify columns to be selected for a select box's options
+ for a class by :
+
+ __Package__->columns('SelectBox' => qw/col1 col2/);
+
+If you don't, 'Stringify' columns are used if they exist and lastly 'Essential'
+columns. The 'Primary' column is always the option value. This means don't
+include it in the 'SelectBox' columns unless you want it in the option content.
+
+You can limit rows selected for the select box with a has_a_select_limit sub like so:
+
+ Customer->has_a(pay_plan => "PayPlan");
+ Customer->has_a(pick_fromTopFive => "Movie");
+ sub has_a_select_limit { {
+ pay_plan => "WHERE is_available = 1",
+ pick_fromTopFive => "ORDER BY rank ASC LIMIT 5" }
+ }
+
+If you need complex stringification make a C<stringify_selectbox> sub which
+takes an arrayref. Elements are in order specified in columns('SelectBox')
+or whatever columns list was used. Otherwise, the array is joined on ' '.
+
+=cut
+
+
+=head1 CHANGES
+
+Many by Peter Speltz
+
+
+Version 1.x of this module returned raw HTML instead of
+C<HTML::Element> objects, which made it harder to manipulate the
+HTML before sending it out. If you depend on the old behaviour, set
+C<$Class::DBI::AsForm::OLD_STYLE> to a true value.
+
+=head1 MAINTAINER
+
+Tony Bowden
+
+=head1 ORIGINAL AUTHOR
+
+Simon Cozens
+
+=head1 BUGS and QUERIES
+
+Please direct all correspondence regarding this module to:
+ bug-Class-DBI-AsForm@rt.cpan.org
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright 2003-2004 by Simon Cozens / Tony Bowden
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Class::DBI>, L<Class::DBI::FromCGI>, L<HTML::Element>.
+
+=cut
+
+\r