sub new_related {
my ($self, $rel, $values, $attrs) = @_;
- return $self->search_related($rel)->new($values, $attrs);
+
+ # FIXME - this is a bad position for this (also an identical copy in
+ # set_from_related), but I have no saner way to hook, and I absolutely
+ # want this to throw at least for coderefs, instead of the "insert a NULL
+ # when it gets hard" insanity --ribasushi
+ #
+ # sanity check - currently throw when a complex coderef rel is encountered
+ # FIXME - should THROW MOAR!
+
+ if (ref $self) { # cdbi calls this as a class method, /me vomits
+
+ my $rsrc = $self->result_source;
+ my (undef, $crosstable, $relcols) = $rsrc->_resolve_condition (
+ $rsrc->relationship_info($rel)->{cond}, $rel, $self, $rel
+ );
+
+ $self->throw_exception("Custom relationship '$rel' does not resolve to a join-free condition fragment")
+ if $crosstable;
+
+ if (@{$relcols || []} and @$relcols = grep { ! exists $values->{$_} } @$relcols) {
+ $self->throw_exception(sprintf (
+ "Custom relationship '%s' not definitive - returns conditions instead of values for column(s): %s",
+ $rel,
+ map { "'$_'" } @$relcols
+ ));
+ }
+ }
+
+ my $row = $self->search_related($rel)->new($values, $attrs);
+ return $row;
}
=head2 create_related
sub create_related {
my $self = shift;
my $rel = shift;
-
- $self->throw_exception("Can't call *_related as class methods")
- unless ref $self;
-
- # we need to stop and check if this is at all possible. If this is
- # an extended relationship with an incomplete definition, we should
- # just forbid it right now.
- my $rel_info = $self->result_source->relationship_info($rel);
- if (ref $rel_info->{cond} eq 'CODE') {
- my ($cond, $ext) = $rel_info->{cond}->({
- self_alias => 'me',
- foreign_alias => $rel,
- self_rowobj => $self,
- self_resultsource => $self->result_source,
- foreign_relname => $rel,
- });
- $self->throw_exception("unable to set_from_related - no simplified condition available for '${rel}'")
- unless $ext;
-
- # now we need to make sure all non-identity relationship
- # definitions are overriden.
- my ($argref) = @_;
- while ( my($col, $value) = each %$ext ) {
- $col =~ s/^$rel\.//;
- my $vref = ref $value;
- if ($vref eq 'HASH') {
- if (keys(%$value) && (keys %$value)[0] ne '=' &&
- !exists $argref->{$col}) {
- $self->throw_exception("unable to set_from_related via complex '${rel}' condition on column(s): '${col}'")
- }
- }
- }
- }
-
- my $obj = $self->search_related($rel)->create(@_);
+ my $obj = $self->new_related($rel, @_)->insert;
delete $self->{related_resultsets}->{$rel};
return $obj;
}
sub set_from_related {
my ($self, $rel, $f_obj) = @_;
- my $rel_info = $self->relationship_info($rel)
+ my $rsrc = $self->result_source;
+ my $rel_info = $rsrc->relationship_info($rel)
or $self->throw_exception( "No such relationship ${rel}" );
if (defined $f_obj) {
unless blessed $f_obj and $f_obj->isa($f_class);
}
- my ($cond, $crosstable) = $self->result_source->_resolve_condition
- ($rel_info->{cond}, $f_obj, $rel, $rel);
- $self->throw_exception(
- "Custom relationship '$rel' does not resolve to a join-free condition fragment"
- ) if $crosstable;
+ # FIXME - this is a bad position for this (also an identical copy in
+ # new_related), but I have no saner way to hook, and I absolutely
+ # want this to throw at least for coderefs, instead of the "insert a NULL
+ # when it gets hard" insanity --ribasushi
+ #
+ # sanity check - currently throw when a complex coderef rel is encountered
+ # FIXME - should THROW MOAR!
+ my ($cond, $crosstable, $relcols) = $rsrc->_resolve_condition (
+ $rel_info->{cond}, $f_obj, $rel, $rel
+ );
+ $self->throw_exception("Custom relationship '$rel' does not resolve to a join-free condition fragment")
+ if $crosstable;
+ $self->throw_exception(sprintf (
+ "Custom relationship '%s' not definitive - returns conditions instead of values for column(s): %s",
+ $rel,
+ map { "'$_'" } @$relcols
+ )) if @{$relcols || []};
$self->set_columns($cond);
our $UNRESOLVABLE_CONDITION = \ '1 = 0';
# Resolves the passed condition to a concrete query fragment and a flag
-# indicating whether this is a cross-table condition.
+# indicating whether this is a cross-table condition. Also an optional
+# list of non-triviail values (notmally conditions) returned as a part
+# of a joinfree condition hash
sub _resolve_condition {
my ($self, $cond, $as, $for, $relname) = @_;
self_rowobj => $obj_rel ? $for : undef
});
+ my $cond_cols;
if ($joinfree_cond) {
# FIXME sanity check until things stabilize, remove at some point
);
}
- return wantarray ? ($joinfree_cond, 0) : $joinfree_cond;
+ # see which parts of the joinfree cond are conditionals
+ my $relcol_list = { map { $_ => 1 } $self->related_source($relname)->columns };
+
+ for my $c (keys %$joinfree_cond) {
+ my ($colname) = $c =~ /^ (?: \Q$relalias.\E )? (.+)/x;
+
+ unless ($relcol_list->{$colname}) {
+ push @$cond_cols, $colname;
+ next;
+ }
+
+ if (
+ ref $joinfree_cond->{$c}
+ and
+ ref $joinfree_cond->{$c} ne 'SCALAR'
+ and
+ ref $joinfree_cond->{$c} ne 'REF'
+ ) {
+ push @$cond_cols, $colname;
+ next;
+ }
+ }
+
+ return wantarray ? ($joinfree_cond, 0, $cond_cols) : $joinfree_cond;
}
else {
return wantarray ? ($crosstable_cond, 1) : $crosstable_cond;
}
}
-
# Accepts one or more relationships for the current source and returns an
# array of column names for each of those relationships. Column names are
# prefixed relative to the current source, in accordance with where they appear
# try to create_related a 80s cd
throws_ok {
$artist->create_related('cds_80s', { title => 'related creation 1' });
-} qr/\Qunable to set_from_related via complex 'cds_80s' condition on column(s): 'year'/, 'Create failed - complex cond';
+} qr/\QCustom relationship 'cds_80s' not definitive - returns conditions instead of values for column(s): 'year'/,
+'Create failed - complex cond';
# now supply an explicit arg overwriting the ambiguous cond
my $id_2020 = $artist->create_related('cds_80s', { title => 'related creation 2', year => '2020' })->id;
# try a specific everything via a non-simplified rel
throws_ok {
$artist->create_related('cds_90s', { title => 'related_creation 4', year => '2038' });
-} qr/\Qunable to set_from_related - no simplified condition available for 'cds_90s'/, 'Create failed - non-simplified rel';
+} qr/\QCustom relationship 'cds_90s' does not resolve to a join-free condition fragment/,
+'Create failed - non-simplified rel';
# Do a self-join last-entry search
my @last_tracks;