Skip to content

Commit

Permalink
Add foreign_resultobj to the customrel signature
Browse files Browse the repository at this point in the history
Tests in next commit
  • Loading branch information
ribasushi committed Jun 11, 2014
1 parent a5f5e47 commit 1adbd3f
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 7 deletions.
3 changes: 3 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions lib/DBIx/Class/Relationship/Base.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 22 additions & 7 deletions lib/DBIx/Class/ResultSource.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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'
);
}

Expand All @@ -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;
Expand Down
13 changes: 13 additions & 0 deletions t/lib/DBICTest/Util.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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;
}

Expand Down

0 comments on commit 1adbd3f

Please sign in to comment.