X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=lib%2FMaypole%2FModel%2FCDBI%2FAsForm.pm;h=8a7f06c95e3f8237aad4d82009b0eba928353060;hb=ee812d68b34c0e43d89551e39ebf5b7d247ac5b6;hp=267cbeaff12e15e92369816974967b68736ab3b4;hpb=ef5744f35f1e0f37a16d945b9ba8c0a5ed76d296;p=maypole.git diff --git a/lib/Maypole/Model/CDBI/AsForm.pm b/lib/Maypole/Model/CDBI/AsForm.pm index 267cbea..8a7f06c 100644 --- a/lib/Maypole/Model/CDBI/AsForm.pm +++ b/lib/Maypole/Model/CDBI/AsForm.pm @@ -1,86 +1,27 @@ 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; +use Carp qw/cluck/; our $OLD_STYLE = 0; -# pjs -- Added new methods to @EXPORT our @EXPORT = qw( - to_cgi to_field make_element_foreign unselect_element + to_cgi to_field foreign_input_delimiter search_inputs unselect_element _field_from_how _field_from_relationship _field_from_column - _to_textarea _to_textfield _to_select _select_guts + _to_textarea _to_textfield _to_select _select_guts _to_foreign_inputs _to_enum_select _to_bool_select _to_hidden _to_link_hidden _rename_foreign_input _to_readonly _options_from_objects _options_from_arrays _options_from_hashes - _options_from_scalars + _options_from_array _options_from_hash ); - -our @EXPORTOK = - qw( - - - ); - - - -our $VERSION = '.09'; -# 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) - -# 10-18-05 - made _to_enum_select check column_default -# 10-19-05 - exported _to_select_from_objs -# - Now VERSION .06 -# 10-24-05 - _to_select_from_many Redesign. -# Now first arg is either a has_many accessor or a array ref of -# objects to select from and the options are in named list . -# selected : object or id -# name : the element name -# to_select_from_many ($accssr|$objs [, selected => $obj|$id, name => $elmnt_name]) -# -# - _to_hidden -- if object arg then name and value are from pk -# _ _rename_foreign_input -- took out useless assignment on new name -# - _to_select : put empty option if column is nullable -# 11-04-05 - _to_readonly with no args makes the calling object pk and id -# - _to_select : if object calls it without a column argument, it make# s a select box of the calling class rows and the object is pre selected. -# -# 11-05-05 - added _to_link_hidden to make a link to the hidden object -# - fixed _to_hidden when called with no args. Hides self obj. -# 11-04-05 - _to_textfield: tries to call "deflate4edit" if column is has_a -# 11-08-05 - Changed Version to .08 - - - -# 1-10-06 -- fixed bug in to_textfiled that was stringifyingf CDBI objects -# # -# -# 1-20-06 - to_select - call db_Main with has a class. -# 1-24-06 - to_select_from_many now named _to_select_from_many . Old deprecated -# - hasmany_class removed in favor of model's related_class method. -# - took out do_select. That is a model action. -# - use search_columns instead of search_fields now. -# - use to_field('column', 'select', {args}) instead of a_select_box. -# -- took out make_hidden_element.was my own personal hack -# -- added _box from DH's FormView to calculate decent textarea size -# -- Refactor to_field into _from_* method calls. -# -# 1-25-06 -- Added _to_checkbox and _to_radio from FView -# 1-27-06 -- Refactored into yet more exported methods -# 1-28-06 -- select constraints where, join order by -# 2-16-05 -- select box cols should only contain pks if you want them to -# be in he content string of the option. Went backt to old way. -# +our $VERSION = '.97'; =head1 NAME @@ -103,58 +44,107 @@ Maypole::Model:CDBI::AsForm - Produce HTML form elements for database columns end_form; } - . . . somewhere use to_field($col, $how, $args) - package BeerDB::Pint; - __PACKAGE__->has_a('drinker', 'BeerDB::Drinker'); - __PACKAGE__->has_a('beer', 'BeerDB::Beer'); - package BeerDB::Drinker; - __PACKAGE__->has_many('pints', 'BeerDB::Pint'); - - - # NOTE NEED to do mapping - - # Order a round -- multiple select of all pints if class method - my $sel = BeerDB::Drinker->to_field('pints', 'select') # - - # Take one down pass it around - my $choice = $Drunk->to_field('pints', 'select'); # Choose from what we already have - + . . . -package Job; + # Somewhere else in a Maypole application about beer... -__PACKAGE__->has_a('employer' => 'Employer'); -__PACKAGE__->has_a('contact' => 'Contact') -package Contact; -__PACKAGE__->has_a('employer_also' => 'Employer'); -__PACKAGE__->has_many('jobs' => 'Job', - { join => { employer => 'employer_also' }, - constraint => { 'finshed' => 0 }, - order_by => "created ASC", - } -); -package Employer; + $beer->to_field('brewery', 'textfield', { + name => 'brewery_id', value => $beer->brewery, + # however, no need to set value since $beer is object + }); -__PACKAGE__->has_many('jobs' => 'Job',); -__PACKAGE__->has_many('contacts' => 'Contact', - order_by => 'name DESC', -); + # Rate a beer + $beer->to_field(rating => select => { + items => [1 , 2, 3, 4, 5], + }); + # Select a Brewery to visit in the UK + Brewery->to_field(brewery_id => { + items => [ Brewery->search_like(location => 'UK') ], + }); - # Below gives select boxes with the multiple attribute. - my $select_jobs_for_new_contact = - Contact->to_field('jobs', 'select'); # Uses constraint and order by + # Make a select for a boolean field + $Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); - my $edit_jobs_for_existing_contact = - $contact->to_field('jobs', 'select'); + $beer->to_field('brewery', { + selected => $beer->brewery, # again not necessary since caller is obj. + }); + $beer->to_field('brewery', 'link_hidden', {r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery}); + # an html link that is also a hidden input to the object. R is required to + # make the uri unless you pass a uri + + + + ##################################################### + # Templates Usage + +
+ + ... + + + + . . . + + + + . . . + +
+ + + ##################################################### + # Advanced Usage + + # has_many select + package Job; + __PACKAGE__->has_a('job_employer' => 'Employer'); + __PACKAGE__->has_a('contact' => 'Contact') + + package Contact; + __PACKAGE__->has_a('cont_employer' => 'Employer'); + __PACKAGE__->has_many('jobs' => 'Job', + { join => { job_employer => 'cont_employer' }, + constraint => { 'finshed' => 0 }, + order_by => "created ASC", + } + ); + + package Employer; + __PACKAGE__->has_many('jobs' => 'Job',); + __PACKAGE__->has_many('contacts' => 'Contact', + order_by => 'name DESC', + ); + + + # Choose some jobs to add to a contact (has multiple attribute). + my $job_sel = Contact->to_field('jobs'); # Uses constraint and order by + + + # Choose a job from $contact->jobs + my $job_sel = $contact->to_field('jobs'); + + 1; + - # Random uses - =head1 DESCRIPTION @@ -166,126 +156,309 @@ into textareas, and fields with a has-a relationship to other C 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-derived class. +=head1 ARGUMENTS HASH +This provides a convenient way to tweak AsForm's behavior in exceptional or +not so exceptional instances. Below describes the arguments hash and +example usages. + $beer->to_field($col, $how, $args); + $beer->to_field($col, $args); -=head2 unselect_element +Not all _to_* methods pay attention to all arguments. For example, '_to_textfield' does not look in $args->{'items'} at all. -Unselects all options in a HTML::Element of type select. -It does nothing if element is not a select element. +=over -=cut +=item name -- the name the element will have , this trumps the derived name. -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'); - } - } -} + $beer->to_field('brewery', 'readonly', { + name => 'brewery_id' + }); +=item value -- the initial value the element will have, trumps derived value -# 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; -} + $beer->to_field('brewery', 'textfield', { + name => 'brewery_id', value => $beer->brewery, + # however, no need to set value since $beer is object + }); +=item items -- array of items generally used to make select box options +Can be array of objects, hashes, arrays, or strings, or just a hash. -=head2 to_cgi + # Rate a beer + $beer->to_field(rating => select => { + items => [1 , 2, 3, 4, 5], + }); + + # Select a Brewery to visit in the UK + Brewery->to_field(brewery_id => { + items => [ Brewery->search_like(location => 'UK') ], + }); + + # Make a select for a boolean field + $Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); + +=item selected -- something representing which item is selected in a select box + + $beer->to_field('brewery', { + selected => $beer->brewery, # again not necessary since caller is obj. + }); + +Can be an simple scalar id, an object, or an array of either + +=item class -- the class for which the input being made for field pertains to. + +This in almost always derived in cases where it may be difficult to derive, -- + # Select beers to serve on handpump + Pub->to_field(handpumps => select => { + class => 'Beer', order_by => 'name ASC', multiple => 1, + }); -This returns a hash mapping all the column names of the class to -HTML::Element objects representing form widgets. +=item column_type -- a string representing column type -pjs -- added a columns list argument to specify which columns to make -inputs for. + $pub->to_field('open', 'bool_select', { + column_type => "bool('Closed', 'Open'), + }); + +=item column_nullable -- flag saying if column is nullable or not + +Generally this can be set to get or not get a null/empty option added to +a select box. AsForm attempts to call "$class->column_nullable" to set this +and it defaults to true if there is no shuch method. + + $beer->to_field('brewery', { column_nullable => 1 }); + +=item r or request -- the Mapyole request object + +=item uri -- uri for a link , used in methods such as _to_link_hidden + + $beer->to_field('brewery', 'link_hidden', + {r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery}); + # an html link that is also a hidden input to the object. R is required to + # make the uri unless you pass a uri + +=item order_by, constraint, join + +These are used in making select boxes. order_by is a simple order by clause +and constraint and join are hashes used to limit the rows selected. The +difference is that join uses methods of the object and constraint uses +static values. You can also specify these in the relationship definitions. +See the relationships documentation of how to set arbitrayr meta info. + + BeerDB::LondonBeer->has_a('brewery', 'BeerDB::Brewery', + order_by => 'brewery_name ASC', + constraint => {location => 'London'}, + 'join' => {'brewery_tablecolumn => 'beer_obj_column'}, + ); + +=item no_hidden_constraints -- + +Tell AsForm not to make hidden inputs for relationship constraints. It does +this sometimes when making foreign inputs. However, i think it should not +do this and that the FromCGI 's _create_related method should do it. + +=back + +=head2 to_cgi + + $self->to_cgi([@columns, $args]); + +This returns a hash mapping all the column names to HTML::Element objects +representing form widgets. It takes two opitonal arguments -- a list of +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. + + $self->to_cgi(); # uses $self->columns; # most used + $self->to_cgi(qw/brewery style rating/); # sometimes + # and on rare occassions this is desireable if you have a lot of fields + # and dont want to call to_field a bunch of times just to tweak one or + # two of them. + $self->to_cgi(@cols, {brewery => { + how => 'textfield' # too big for select + }, + style => { + column_nullable => 0, + how => 'select', + items => ['Ale', 'Lager'] + } + }); =cut sub to_cgi { - my ($class, @columns) = @_; # pjs -- added columns arg - @columns = $class->columns unless (@columns); - map { $_ => $class->to_field($_) } @columns; + my ($class, @columns) = @_; + my $args = {}; + if (not @columns) { + @columns = $class->columns; + # Eventually after stabalization, we could add display_columns + #keys map { $_ => 1 } ($class->display_columns, $class->columns); + } else { + if ( ref $columns[-1] eq 'HASH' ) { + $args = pop @columns; + } + } + map { $_ => $class->to_field($_, $args->{$_}) } @columns; } - -=head2 to_field($field [, $how]) +=head2 to_field($field [, $how][, $args]) This maps an individual column to a form element. The C argument -can be used to force the field type into one of C, C