From: Peter Rabbitson Date: Wed, 11 Jun 2014 14:32:59 +0000 (+0200) Subject: Add foreign_resultobj to the customrel signature X-Git-Tag: v0.082800~177 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=1adbd3fc463c963d77fa2755eccaf6112e63487a;p=dbsrgits%2FDBIx-Class.git Add foreign_resultobj to the customrel signature Tests in next commit --- diff --git a/Changes b/Changes index 055c6f4..b1b040f 100644 --- a/Changes +++ b/Changes @@ -7,6 +7,9 @@ Revision history for DBIx::Class returned from storage - Custom condition relationships are now invoked with a slightly different signature (existing coderefs will continue to work) + - Add extra custom condition coderef attribute 'foreign_resultobj' + to allow for proper reverse-relationship emulation + (i.e. $result->set_from_related($custom_cond, $foreign_resultobj) * Fixes - Fix Resultset delete/update affecting *THE ENTIRE TABLE* in cases diff --git a/lib/DBIx/Class/Relationship/Base.pm b/lib/DBIx/Class/Relationship/Base.pm index a2e78ff..e11263f 100644 --- a/lib/DBIx/Class/Relationship/Base.pm +++ b/lib/DBIx/Class/Relationship/Base.pm @@ -238,7 +238,10 @@ metadata. Currently the supplied coderef is executed as: self_alias => The alias of the invoking resultset foreign_alias => The alias of the to-be-joined resultset (does *NOT* always match rel_name) + # only one of these (or none at all) will ever be supplied to aid in the + # construction of a join-free condition self_resultobj => The invocant object itself in case of a $resultobj->$rel_name() call + foreign_resultobj => The related object in case of $resultobj->set_from_related($rel_name, $foreign_resultobj) # deprecated inconsistent names, will be forever available for legacy code self_rowobj => Old deprecated slot for self_resultobj diff --git a/lib/DBIx/Class/ResultSource.pm b/lib/DBIx/Class/ResultSource.pm index 4f45c58..9f6ac9c 100644 --- a/lib/DBIx/Class/ResultSource.pm +++ b/lib/DBIx/Class/ResultSource.pm @@ -1776,34 +1776,49 @@ sub _resolve_relationship_condition { self_resultsource => $self, self_alias => $args->{self_alias}, foreign_alias => $args->{foreign_alias}, - self_resultobj => defined $args->{self_resultobj} ? $args->{self_resultobj} : undef, + self_resultobj => (defined $args->{self_resultobj} ? $args->{self_resultobj} : undef), + foreign_resultobj => (defined $args->{foreign_resultobj} ? $args->{foreign_resultobj} : undef), }; # legacy - never remove these!!! $cref_args->{foreign_relname} = $cref_args->{rel_name}; $cref_args->{self_rowobj} = $cref_args->{self_resultobj}; - my ($crosstable_cond, $joinfree_cond) = $args->{condition}->($cref_args); + my ($crosstable_cond, $joinfree_cond, @extra) = $args->{condition}->($cref_args); + + # FIXME sanity check + carp_unique('A custom condition coderef can return at most 2 conditions: extra return values discarded') + if @extra; my @nonvalue_cols; if ($joinfree_cond) { + my ($joinfree_alias, $joinfree_source); + if (defined $args->{self_resultobj}) { + $joinfree_alias = $args->{foreign_alias}; + $joinfree_source = $self->related_source($args->{rel_name}); + } + elsif (defined $args->{foreign_resultobj}) { + $joinfree_alias = $args->{self_alias}; + $joinfree_source = $self; + } + # FIXME sanity check until things stabilize, remove at some point $self->throw_exception ( "A join-free condition returned for relationship '$args->{rel_name}' without a result object to chain from" - ) unless defined $args->{self_resultobj}; + ) unless $joinfree_alias; - my $foreign_src_fq_col_list = { map { ( "$args->{foreign_alias}.$_" => 1 ) } $self->related_source($args->{rel_name})->columns }; + my $fq_col_list = { map { ( "$joinfree_alias.$_" => 1 ) } $joinfree_source->columns }; # FIXME another sanity check if ( ref $joinfree_cond ne 'HASH' or - grep { ! $foreign_src_fq_col_list->{$_} } keys %$joinfree_cond + grep { ! $fq_col_list->{$_} } keys %$joinfree_cond ) { $self->throw_exception ( "The join-free condition returned for relationship '$args->{rel_name}' must be a hash " - .'reference with all keys being fully qualified column names of the foreign source' + .'reference with all keys being fully qualified column names of the corresponding source' ); } @@ -1813,7 +1828,7 @@ sub _resolve_relationship_condition { @{ $self->schema->storage->_extract_fixed_condition_columns($joinfree_cond) } }; @nonvalue_cols = map - { $_ =~ /^\Q$args->{foreign_alias}.\E(.+)/ } + { $_ =~ /^\Q$joinfree_alias.\E(.+)/ } grep { ! $joinfree_cond_equality_columns->{$_} } keys %$joinfree_cond; diff --git a/t/lib/DBICTest/Util.pm b/t/lib/DBICTest/Util.pm index b821243..847fba8 100644 --- a/t/lib/DBICTest/Util.pm +++ b/t/lib/DBICTest/Util.pm @@ -66,7 +66,10 @@ sub check_customcond_args ($) { confess "Passed resultsource has no record of the supplied rel_name - likely wrong \$rsrc" unless ref $args->{self_resultsource}->relationship_info($args->{rel_name}); + my $rowobj_cnt = 0; + if (defined $args->{self_resultobj} or defined $args->{self_rowobj} ) { + $rowobj_cnt++; for (qw(self_resultobj self_rowobj)) { confess "Custom condition argument '$_' must be a result instance" unless defined blessed $args->{$_} and $args->{$_}->isa('DBIx::Class::Row'); @@ -76,6 +79,16 @@ sub check_customcond_args ($) { if refaddr($args->{self_resultobj}) != refaddr($args->{self_rowobj}); } + if (defined $args->{foreign_resultobj}) { + $rowobj_cnt++; + + confess "Custom condition argument 'foreign_resultobj' must be a result instance" + unless defined blessed $args->{foreign_resultobj} and $args->{foreign_resultobj}->isa('DBIx::Class::Row'); + } + + confess "Result objects supplied on both ends of a relationship" + if $rowobj_cnt == 2; + $args; }