Index: /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/update/has_one_select.yml =================================================================== --- /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/update/has_one_select.yml (revision 0) +++ /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/update/has_one_select.yml (revision 0) @@ -0,0 +1,17 @@ +--- +auto_fieldset: 1 + +elements: + - type: Hidden + name: id + + - type: Text + name: text_col + + - type: Select + name: user + model_config: + resultset: User + + - type: Submit + name: submit Index: /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/update/has_one.t =================================================================== --- /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/update/has_one.t (revision 1485) +++ /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/update/has_one.t (working copy) @@ -1,6 +1,6 @@ use strict; use warnings; -use Test::More tests => 4; +use Test::More tests => 9; use HTML::FormFu; use lib 't/lib'; @@ -64,3 +64,51 @@ is( $user->name, 'bar' ); } +$form = HTML::FormFu->new; + +$form->load_config_file('t/update/has_one_select.yml'); + +$form->stash->{schema} = $schema; + +{ + $form->process( { + "id" => 3, + "text_col" => 'a', + "user" => 1, + } ); + + ok( $form->submitted_and_valid ); + + my $row = $schema->resultset('Master')->find(3); + + $form->model->update($row); + + is($row->user->id, 1); + + $form->process( { + "id" => 3, + "text_col" => 'a', + "user" => 99, + } ); + + ok( $form->submitted_and_valid ); + + my $row = $schema->resultset('Master')->find(3); + + $form->model->update($row); + + is($row->user->id, 1); + + $form = HTML::FormFu->new; + + $form->stash->{schema} = $schema; + + $form->load_config_file('t/update/has_one_select.yml'); + + $form->model->default_values($row); + + $form->process; + + like($form, qr/value="1" selected=/); +} + Index: /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/lib/DBICTestLib.pm =================================================================== --- /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/lib/DBICTestLib.pm (revision 1485) +++ /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/t/lib/DBICTestLib.pm (working copy) @@ -75,7 +75,7 @@ $dbh->do( <add_columns( id => { data_type => "INTEGER", is_nullable => 0 }, - master => { data_type => "INTEGER", is_nullable => 0 }, + master => { data_type => "INTEGER", is_nullable => 1 }, name => { data_type => "TEXT", is_nullable => 0 }, title => { data_type => "TEXT" }, ); Index: /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/lib/HTML/FormFu/Model/DBIC.pm =================================================================== --- /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/lib/HTML/FormFu/Model/DBIC.pm (revision 1485) +++ /Users/mo/Documents/workspace/HTML-FormFu-Model-DBIC/lib/HTML/FormFu/Model/DBIC.pm (working copy) @@ -217,10 +217,13 @@ if ( !defined $info or $info->{attrs}{accessor} eq 'multi' ) { my @defaults = $dbic->$name->get_column($col)->all; $field->default( \@defaults ); + } else { + # has_one/might_have + my($pk) = $dbic->result_source->primary_columns; + $field->default( $dbic->$name->$pk ); } } else { - # This field is a method expected to return the value $field->default( $dbic->$name ); } @@ -442,14 +445,13 @@ return if $attrs->{no_follow}; for my $rel (@$rels) { - # don't follow rels to where we came from next if defined $attrs->{from} && $attrs->{from} eq $rs->related_source($rel)->result_class; - + my ($block) - = grep { !$_->is_field } + = grep { !$_->is_field || $_->multi_value } @{ $base->get_all_elements( { nested_name => $rel } ) }; next if !defined $block; @@ -481,6 +483,41 @@ from => $dbic->result_class, } ); } + else { + # has_one or might_have relationship + + my $info = $dbic->result_source->relationship_info($rel); + + my @fpkey = $dbic->related_resultset($rel)->result_source->primary_columns; + + croak 'multiple primary keys are not supported for has_one/might_have relationships' + if(@fpkey > 1); + + my $fpkey = shift @fpkey; + my ($fkey, $skey) = %{$info->{cond}}; + $fkey =~ s/^foreign\.//; + $skey =~ s/^self\.//; + + my $fclass = $info->{class}; + + croak 'The primary key and the foreign key may not be the same column in class '.$fclass + if $fpkey eq $fkey; + + my $schema = $dbic->result_source->schema; + # use transactions if supported by storage + $schema->txn_do(sub { + + # reset any previous items which were related to $dbic + $rs->schema->resultset($fclass)->search({ $fkey => $dbic->$skey })->update({ $fkey => undef }); + + # set new related item + my $updated = $rs->schema->resultset($fclass)->search( { $fpkey => $params } )->update({ $fkey => $dbic->$skey }); + + $schema->txn_rollback + if $updated != 1; + + }); + } } } @@ -744,7 +781,9 @@ { $dbic->set_column( $accessor, $value ); } - elsif ( $dbic->can($accessor) ) { + elsif ( $dbic->can($accessor) + # and $accessor is not a has_one or might_have rel where the foreign key is on the foreign table + and !$dbic->result_source->relationship_info($accessor)) { $dbic->$accessor($value); } else { @@ -1164,7 +1203,8 @@ __PACKAGE__->table("review"); __PACKAGE__->add_columns( - book => { data_type => "INTEGER" }, + id => { data_type => "INTEGER" }, + book => { data_type => "INTEGER", is_nullable => 1 }, review_text => { data_type => "TEXT" }, ); @@ -1190,6 +1230,23 @@ to have a field for the related table's primary key, as DBIx::Class will handle retrieving the correct row automatically. +You can also set a C or C relationship using a multi value +field like L. + + elements: + - type: Text + name: title + + - type: Select + nested: review + model_config: + resultset: Review + +This will load all reviews into the select field. If you select a review from +that list, a current relationship to a review is removed and the new one is +added. This requires that the primary key of the C table and the +foreign key do not match. + =head2 has_many and many_to_many relationships The general principle is the same as for C and C above,