Class-DBI-FormBuilder
view release on metacpan or search on metacpan
lib/Class/DBI/FormBuilder.pm view on Meta::CPAN
my $cols = {};
my $data = $form->fields;
foreach my $column ( $them->columns('All') )
{
next unless exists $data->{ $column->name };
$cols->{ $column->mutator } = $data->{ $column->name };
}
return $cols;
}
=item create_from_multiform
Creates multiple new objects from a C<as_multiform> form submission.
=cut
# TODO: check if we need to call _update_many_many
sub create_from_multiform
{
my ( $them, $form ) = @_;
Carp::croak "create_from_multiform can only be called as a class method" if ref $them;
return unless $form->submitted && $form->validate;
my $form_data = $form->field;
my $items_data;
foreach my $fname ( keys %$form_data )
{
$fname =~ /^R(\d+)__(\w+)$/;
my $item_num = $1;
my $col_name = $2;
my $mutator = $them->find_column( $col_name )->mutator;
$items_data->{ $item_num }->{ $mutator } = $form_data->{ $fname };
}
my @new = map { $them->create( $_ ) } values %$items_data;
return @new;
}
=item update_from_form( $form )
Updates an existing CDBI object.
If called on an object, will update that object.
If called on a class, will first retrieve the relevant object (via C<retrieve_from_form>).
=cut
sub update_from_form
{
my ( $proto, $form ) = @_;
my $them = ref( $proto ) ? $proto : $proto->retrieve_from_form( $form );
Carp::croak "No object found matching submitted primary key data" unless $them;
my $me = $proto->__form_builder_subclass__;
$me->_run_update( $them, $form );
$me->_update_many_to_many( $them, $form );
return $them;
}
sub _run_update
{
my ( $me, $them, $fb ) = @_;
return unless $fb->submitted && $fb->validate;
my $formdata = $fb->fields;
# I think this is now unnecessary (0.4), because pks are in keepextras
delete $formdata->{ $_ } for map {''.$_} $them->primary_columns;
# assumes no extra fields in the form
#$them->set( %$formdata );
# Start with all possible columns. Only ask for the subset represented
# in the form. This allows correct handling of fields that result in
# 'missing' entries in the submitted data - e.g. checkbox groups with
# no item selected will not even appear in the raw request data, but here
# they should result in an undef value being sent to the object.
my %coldata = map { $_->mutator => $formdata->{ $_->name } }
grep { exists $formdata->{ $_->name } }
$them->columns( 'All' );
$them->set( %coldata );
$them->update;
return $them;
}
# from Ron McClain:
sub _update_many_to_many
{
my ( $me, $obj, $form ) = @_;
my $has_many = $obj->meta_info('has_many') || return;
foreach my $field ( keys %{ $form->fields } )
{
next unless $has_many->{$field};
# many-many
next unless $has_many->{$field}->{args}->{mapping};
my $mkey = $has_many->{$field}->{args}->{mapping}->[0];
my $fkey = $has_many->{$field}->{args}->{foreign_key};
my $fclass = $has_many->{$field}->{foreign_class};
my %rel_exists;
foreach my $rel ( $fclass->search( $fkey => $obj->id ) )
{
if ( grep { $rel->$mkey->id == $_ } $form->field($field) )
{
$rel_exists{ $rel->$mkey->id }++;
}
else
{
$rel->delete;
}
}
foreach my $val ( $form->field($field) )
{
$fclass->create( { $fkey => $obj->id,
$mkey => $val,
} )
unless $rel_exists{$val};
}
}
}
# Also, this patch only applies to many-many. Not one-many. I got to
# thinking about it, and it doesn't make sense to me have a select list
# for one-many with existing records.. Because if you edit a record and
# select a record to relate to it.. The related record may already be
# associated with a separate record, and it would kill that association..
# Not intuitive. But for many-many, I don't see the downside of having
# something like this be standard. The only thing I can think of is, what
# if the glue table has additional columns besides the two foreign keys?
# I can't think of an example right now, but I guess it's possible. The
# other thing I don't quite understand is why
# meta_info->field->args->mapping is an array and not a scalar. I just
# pull off the first element, but I don't know whether it's possible that
# there be more elements than that, and what they mean.
=item update_or_create_from_form
Class method.
Attempts to look up an object (using primary key data submitted in the form) and update it.
If none exists (or if no values for primary keys are supplied), a new object is created.
=cut
sub update_or_create_from_form
{
my ( $them, $form ) = @_;
Carp::croak "update_or_create_from_form can only be called as a class method" if ref $them;
$them->__form_builder_subclass__->_run_update_or_create_from_form( $them, $form );
}
sub _run_update_or_create_from_form
{
my ( $me, $them, $form ) = @_;
return unless $form->submitted && $form->validate;
my $object = $them->retrieve_from_form( $form );
return $object->update_from_form( $form ) if $object;
$them->create_from_form( $form );
}
=back
=head2 Search methods
Note that search methods (except for C<retrieve_from_form>) will return a CDBI iterator
in scalar context, and a (possibly empty) list of objects in list context.
All the search methods except C<retrieve_from_form> require that the submitted form should be
built using C<search_form> (not C<as_form>). Otherwise the form may fail validation checks
because of missing required fields specified by C<as_form> (C<search_form> does not automatically
configure any fields as required).
=over 4
=item retrieve_from_form
Use primary key data in a form to retrieve a single object.
=cut
sub retrieve_from_form
{
my ( $them, $form ) = @_;
Carp::croak "retrieve_from_form can only be called as a class method" if ref $them;
$them->__form_builder_subclass__->_run_retrieve_from_form( $them, $form );
}
sub _run_retrieve_from_form
{
my ( $me, $them, $form ) = @_;
# we don't validate because pk data must side-step validation as it's
# unknowable in advance whether they will even be present.
#return unless $fb->submitted && $fb->validate;
my %pkdata = map { $_ => $form->cgi_param( $_->mutator ) || undef } $them->primary_columns;
( run in 0.532 second using v1.01-cache-2.11-cpan-13bb782fe5a )