X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=dbsrgits%2FDBIx-Class.git;a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FRelationship%2FBase.pm;fp=lib%2FDBIx%2FClass%2FRelationship%2FBase.pm;h=cfd23f27f1a50cccf45f924c90ada261496ea191;hp=c4d4df592c05d3c727a43a02306e4f13a825408e;hb=1bd54f3d4bc8428d602d2e28cb410b303bb242b7;hpb=86be9bcb90213db633791fcce074b7268765f615 diff --git a/lib/DBIx/Class/Relationship/Base.pm b/lib/DBIx/Class/Relationship/Base.pm index c4d4df5..cfd23f2 100644 --- a/lib/DBIx/Class/Relationship/Base.pm +++ b/lib/DBIx/Class/Relationship/Base.pm @@ -8,8 +8,9 @@ use base qw/DBIx::Class/; use Scalar::Util qw/weaken blessed/; use DBIx::Class::_Util qw( UNRESOLVABLE_CONDITION DUMMY_ALIASPAIR - fail_on_internal_call + dbic_internal_try fail_on_internal_call ); +use DBIx::Class::SQLMaker::Util 'extract_equality_conditions'; use DBIx::Class::Carp; use namespace::clean; @@ -530,7 +531,11 @@ sub related_resultset { self_result_object => $self, # an extra sanity check guard - require_join_free_condition => ! $relcond_is_freeform, + require_join_free_condition => !!( + ! $relcond_is_freeform + and + $self->in_storage + ), # an API where these are optional would be too cumbersome, # instead always pass in some dummy values @@ -585,6 +590,7 @@ sub related_resultset { # FIXME - this loop doesn't seem correct - got to figure out # at some point what exactly it does. + # See also the FIXME at the end of new_related() ( ( $reverse->{$_}{attrs}{accessor}||'') eq 'multi' ) ? weaken( $attrs->{related_objects}{$_}[0] = $self ) : weaken( $attrs->{related_objects}{$_} = $self ) @@ -674,16 +680,94 @@ your storage until you call L on it. sub new_related { my ($self, $rel, $data) = @_; - $self->related_resultset($rel)->new_result( $self->result_source->_resolve_relationship_condition ( - infer_values_based_on => $data, + $self->throw_exception( + "Result object instantiation requires a hashref as argument" + ) unless ref $data eq 'HASH'; + + my $rsrc = $self->result_source; + my $rel_rsrc = $rsrc->related_source($rel); + +### +### This section deliberately does not rely on require_join_free_values, +### as quite often the resulting related object is useless without the +### contents of $data mixed in. Originally this code was part of +### resolve_relationship_condition() but given it has a single, very +### context-specific call-site it made no sense to expose it to end users. +### + + my $rel_resolution = $rsrc->_resolve_relationship_condition ( rel_name => $rel, self_result_object => $self, - # an API where these are optional would be too cumbersome, - # instead always pass in some dummy values - DUMMY_ALIASPAIR, + # In case we are *not* in_storage it is ok to treat failed resolution as an empty hash + # This happens e.g. as a result of various in-memory related graph of objects + require_join_free_condition => !! $self->in_storage, + + # dummy aliases with deliberately known lengths, so that we can + # quickly strip them below if needed + foreign_alias => 'F', + self_alias => 'S', + ); + + my $rel_values = + $rel_resolution->{join_free_values} + || + { map { substr( $_, 2 ) => $rel_resolution->{join_free_condition}{$_} } keys %{ $rel_resolution->{join_free_condition} } } + ; + + # mix everything together + my $amalgamated_values = { + %{ + # in case we got back join_free_values - they already have passed the extractor + $rel_resolution->{join_free_values} + ? $rel_values + : extract_equality_conditions( + $rel_values, + 'consider_nulls' + ) + }, + %$data, + }; + + # cleanup possible rogue { somecolumn => [ -and => 1,2 ] } + ($amalgamated_values->{$_}||'') eq UNRESOLVABLE_CONDITION + and + delete $amalgamated_values->{$_} + for keys %$amalgamated_values; + + if( my @nonvalues = grep { ! exists $amalgamated_values->{$_} } keys %$rel_values ) { + + $self->throw_exception( + "Unable to complete value inferrence - relationship '$rel' " + . "on source '@{[ $rsrc->source_name ]}' results " + . 'in expression(s) instead of definitive values: ' + . do { + # FIXME - used for diag only, but still icky + my $sqlm = + dbic_internal_try { $rsrc->schema->storage->sql_maker } + || + ( + require DBIx::Class::SQLMaker + and + DBIx::Class::SQLMaker->new + ) + ; + local $sqlm->{quote_char}; + local $sqlm->{_dequalify_idents} = 1; + ($sqlm->_recurse_where({ map { $_ => $rel_values->{$_} } @nonvalues }))[0] + } + ); + } - )->{inferred_values} ); + # And more complications - in case the relationship did not resolve + # we *have* to loop things through search_related ( essentially re-resolving + # everything we did so far, but with different type of handholding ) + # FIXME - this is still a mess, just a *little* better than it was + # See also the FIXME at the end of related_resultset() + exists $rel_resolution->{join_free_values} + ? $rel_rsrc->result_class->new({ -result_source => $rel_rsrc, %$amalgamated_values }) + : $self->related_resultset($rel)->new_result( $amalgamated_values ) + ; } =head2 create_related @@ -830,7 +914,7 @@ sub set_from_related { my ($self, $rel, $f_obj) = @_; $self->set_columns( $self->result_source->_resolve_relationship_condition ( - infer_values_based_on => {}, + require_join_free_values => 1, rel_name => $rel, foreign_values => ( # maintain crazy set_from_related interface @@ -865,7 +949,7 @@ sub set_from_related { # instead always pass in some dummy values DUMMY_ALIASPAIR, - )->{inferred_values} ); + )->{join_free_values} ); return 1; }