clause of the C<JOIN> statement associated with this relationship.
While every coderef-based condition must return a valid C<ON> clause, it may
-elect to additionally return a simplified join-free condition hashref when
+elect to additionally return a simplified join-free condition hashref when
invoked as C<< $row_object->relationship >>, as opposed to
C<< $rs->related_resultset('relationship') >>. In this case C<$row_object> is
passed to the coderef as C<< $args->{self_rowobj} >>, so a user can do the
=item proxy =E<gt> $column | \@columns | \%column
+The 'proxy' attribute can be used to retrieve values, and to perform
+updates if the relationship has 'cascade_update' set. The 'might_have'
+and 'has_one' relationships have this set by default; if you want a proxy
+to update across a 'belongs_to' relationship, you must set the attribute
+yourself.
+
=over 4
=item \@columns
An arrayref containing a list of accessors in the foreign class to create in
the main class. If, for example, you do the following:
- MyDB::Schema::CD->might_have(liner_notes => 'MyDB::Schema::LinerNotes',
+ MyApp::Schema::CD->might_have(liner_notes => 'MyApp::Schema::LinerNotes',
undef, {
proxy => [ qw/notes/ ],
});
-Then, assuming MyDB::Schema::LinerNotes has an accessor named notes, you can do:
+Then, assuming MyApp::Schema::LinerNotes has an accessor named notes, you can do:
- my $cd = MyDB::Schema::CD->find(1);
+ my $cd = MyApp::Schema::CD->find(1);
$cd->notes('Notes go here'); # set notes -- LinerNotes object is
# created if it doesn't exist
+For a 'belongs_to relationship, note the 'cascade_update':
+
+ MyApp::Schema::Track->belongs_to( cd => 'DBICTest::Schema::CD', 'cd,
+ { proxy => ['title'], cascade_update => 1 }
+ );
+ $track->title('New Title');
+ $track->update; # updates title in CD
+
=item \%column
A hashref where each key is the accessor you want installed in the main class,
and its value is the name of the original in the fireign class.
- MyDB::Schema::Track->belongs_to( cd => 'DBICTest::Schema::CD', 'cd', {
+ MyApp::Schema::Track->belongs_to( cd => 'DBICTest::Schema::CD', 'cd', {
proxy => { cd_title => 'title' },
});
NOTE: you can pass a nested struct too, for example:
- MyDB::Schema::Track->belongs_to( cd => 'DBICTest::Schema::CD', 'cd', {
+ MyApp::Schema::Track->belongs_to( cd => 'DBICTest::Schema::CD', 'cd', {
proxy => [ 'year', { cd_title => 'title' } ],
});
per-relationship basis by supplying C<< cascade_update => 0 >> in
the relationship attributes.
+The C<belongs_to> relationship does not update across relationships
+by default, so if you have a 'proxy' attribute on a belongs_to and want to
+use 'update' on it, you muse set C<< cascade_update => 1 >>.
+
This is not a RDMS style cascade update - it purely means that when
an object has update called on it, all the related objects also
have update called. It will not change foreign keys automatically -
# root alias as 'me', instead of $rel (as opposed to invoking
# $rs->search_related)
-
local $source->{_relationships}{me} = $source->{_relationships}{$rel}; # make the fake 'me' rel
my $obj_table_alias = lc($source->source_name) . '__row';
+ $obj_table_alias =~ s/\W+/_/g;
$source->resultset->search(
$self->ident_condition($obj_table_alias),
my $reverse = $source->reverse_relationship_info($rel);
foreach my $rev_rel (keys %$reverse) {
if ($reverse->{$rev_rel}{attrs}{accessor} && $reverse->{$rev_rel}{attrs}{accessor} eq 'multi') {
- $attrs->{related_objects}{$rev_rel} = [ $self ];
- weaken $attrs->{related_object}{$rev_rel}[0];
+ weaken($attrs->{related_objects}{$rev_rel}[0] = $self);
} else {
- $attrs->{related_objects}{$rev_rel} = $self;
- weaken $attrs->{related_object}{$rev_rel};
+ weaken($attrs->{related_objects}{$rev_rel} = $self);
}
}
}
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);