X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FRelationship%2FBase.pm;h=04b1a88cd838f287ad650f5a311647d73fedd4c1;hb=938b7adc4501c517d84be7daa13aefdb67f346b8;hp=e0007ffee6fa27f263f22fea8e13b2cb98400ba0;hpb=83a6b24431383e560f414f2fcaefe7b8c08e03d2;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Relationship/Base.pm b/lib/DBIx/Class/Relationship/Base.pm index e0007ff..04b1a88 100644 --- a/lib/DBIx/Class/Relationship/Base.pm +++ b/lib/DBIx/Class/Relationship/Base.pm @@ -181,11 +181,32 @@ L and the resulting SQL will be used verbatim as the C clause of the C statement associated with this relationship. While every coderef-based condition must return a valid C clause, it may -elect to additionally return a simplified join-free condition hashref when -invoked as C<< $result->relationship >>, as opposed to -C<< $rs->related_resultset('relationship') >>. In this case C<$result> is -passed to the coderef as C<< $args->{self_resultobj} >>, so a user can do the -following: +elect to additionally return a simplified B join-free condition +consisting of a hashref with B. This boils down to two scenarios: + +=over + +=item * + +When relationship resolution is invoked after C<< $result->$rel_name >>, as +opposed to C<< $rs->related_resultset($rel_name) >>, the C<$result> object +is passed to the coderef as C<< $args->{self_result_object} >>. + +=item * + +Alternatively when the user-space invokes resolution via +C<< $result->set_from_related( $rel_name => $foreign_values_or_object ) >>, the +corresponding data is passed to the coderef as C<< $args->{foreign_values} >>, +B in the form of a hashref. If a foreign result object is supplied +(which is valid usage of L), its values will be extracted +into hashref form by calling L. + +=back + +Note that the above scenarios are mutually exclusive, that is you will be supplied +none or only one of C and C. In other words if +you define your condition coderef as: sub { my $args = shift; @@ -195,14 +216,17 @@ following: "$args->{foreign_alias}.artist" => { -ident => "$args->{self_alias}.artistid" }, "$args->{foreign_alias}.year" => { '>', "1979", '<', "1990" }, }, - $args->{self_resultobj} && { - "$args->{foreign_alias}.artist" => $args->{self_resultobj}->artistid, + ! $args->{self_result_object} ? () : { + "$args->{foreign_alias}.artist" => $args->{self_result_object}->artistid, "$args->{foreign_alias}.year" => { '>', "1979", '<', "1990" }, }, + ! $args->{foreign_values} ? () : { + "$args->{self_alias}.artistid" => $args->{foreign_values}{artist}, + } ); } -Now this code: +Then this code: my $artist = $schema->resultset("Artist")->find({ id => 4 }); $artist->cds_80s->all; @@ -219,34 +243,46 @@ With the bind values: '4', '1990', '1979' -Note that in order to be able to use -L<< $result->create_related|DBIx::Class::Relationship::Base/create_related >>, -the coderef must not only return as its second such a "simple" condition -hashref which does not depend on joins being available, but the hashref must -contain only plain values/deflatable objects, such that the result can be -passed directly to L. For -instance the C constraint in the above example prevents the relationship -from being used to create related objects (an exception will be thrown). +While this code: + + my $cd = $schema->resultset("CD")->search({ artist => 1 }, { rows => 1 })->single; + my $artist = $schema->resultset("Artist")->new({}); + $artist->set_from_related('cds_80s'); + +Will properly set the C<< $artist->artistid >> field of this new object to C<1> + +Note that in order to be able to use L (and by extension +L<< $result->create_related|DBIx::Class::Relationship::Base/create_related >>), +the returned join free condition B contain only plain values/deflatable +objects. For instance the C constraint in the above example prevents +the relationship from being used to create related objects using +C<< $artst->create_related( cds_80s => { title => 'blah' } ) >> (an +exception will be thrown). In order to allow the user to go truly crazy when generating a custom C clause, the C<$args> hashref passed to the subroutine contains some extra metadata. Currently the supplied coderef is executed as: $relationship_info->{cond}->({ - self_resultsource => The resultsource instance on which rel_name is registered - rel_name => The relationship name (does *NOT* always match foreign_alias) + self_resultsource => The resultsource instance on which rel_name is registered + rel_name => The relationship name (does *NOT* always match foreign_alias) - self_alias => The alias of the invoking resultset - foreign_alias => The alias of the to-be-joined resultset (does *NOT* always match rel_name) + 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) + + self_result_object => The invocant *object* itself in case of a call like + $result_object->$rel_name( ... ) + + foreign_values => A *hashref* of related data: may be passed in directly or + derived via ->get_columns() from a related object in case of + $result_object->set_from_related( $rel_name, $foreign_result_object ) # deprecated inconsistent names, will be forever available for legacy code - self_rowobj => Old deprecated slot for self_resultobj - foreign_relname => Old deprecated slot for rel_name + self_rowobj => Old deprecated slot for self_result_object + foreign_relname => Old deprecated slot for rel_name }); =head3 attributes @@ -370,7 +406,7 @@ the relationship attributes. The C 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 >>. +use 'update' on it, you must 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 @@ -463,39 +499,29 @@ this instance (like in the case of C relationships). =cut sub related_resultset { - my $self = shift; + $_[0]->throw_exception( + '$result->related_resultset() no longer accepts extra search arguments, ' + . 'you need to switch to ...->related_resultset($relname)->search_rs(...) ' + . 'instead (it was never documented and more importantly could never work ' + . 'reliably due to the heavy caching involved)' + ) if @_ > 2; - $self->throw_exception("Can't call *_related as class methods") - unless ref $self; + $_[0]->throw_exception("Can't call *_related as class methods") + unless ref $_[0]; - my $rel = shift; + return $_[0]->{related_resultsets}{$_[1]} + if defined $_[0]->{related_resultsets}{$_[1]}; - return $self->{related_resultsets}{$rel} - if defined $self->{related_resultsets}{$rel}; + my ($self, $rel) = @_; return $self->{related_resultsets}{$rel} = do { - my $rel_info = $self->relationship_info($rel) - or $self->throw_exception( "No such relationship '$rel'" ); - - my $attrs = (@_ > 1 && ref $_[$#_] eq 'HASH' ? pop(@_) : {}); - $attrs = { %{$rel_info->{attrs} || {}}, %$attrs }; - - $self->throw_exception( "Invalid query: @_" ) - if (@_ > 1 && (@_ % 2 == 1)); - my $query = ((@_ > 1) ? {@_} : shift); - my $rsrc = $self->result_source; - # condition resolution may fail if an incomplete master-object prefetch - # is encountered - that is ok during prefetch construction (not yet in_storage) - my ($cond, $is_crosstable) = try { - $rsrc->_resolve_condition( $rel_info->{cond}, $rel, $self, $rel ) - } - catch { - $self->throw_exception ($_) if $self->in_storage; - UNRESOLVABLE_CONDITION; # RV, no return() - }; + my $rel_info = $rsrc->relationship_info($rel) + or $self->throw_exception( "No such relationship '$rel'" ); + + my ($cond, $is_crosstable) = $rsrc->_resolve_condition( $rel_info->{cond}, $rel, $self, $rel ); # keep in mind that the following if() block is part of a do{} - no return()s!!! if ($is_crosstable and ref $rel_info->{cond} eq 'CODE') { @@ -510,16 +536,23 @@ sub related_resultset { # root alias as 'me', instead of $rel (as opposed to invoking # $rs->search_related) - local $rsrc->{_relationships}{me} = $rsrc->{_relationships}{$rel}; # make the fake 'me' rel + # make the fake 'me' rel + local $rsrc->{_relationships}{me} = { + %{ $rsrc->{_relationships}{$rel} }, + _original_name => $rel, + }; + my $obj_table_alias = lc($rsrc->source_name) . '__row'; $obj_table_alias =~ s/\W+/_/g; $rsrc->resultset->search( $self->ident_condition($obj_table_alias), { alias => $obj_table_alias }, - )->search_related('me', $query, $attrs) + )->search_related('me', undef, $rel_info->{attrs}) } else { + my $attrs = { %{ $rel_info->{attrs} } }; + # FIXME - this conditional doesn't seem correct - got to figure out # at some point what it does. Also the entire UNRESOLVABLE_CONDITION # business seems shady - we could simply not query *at all* @@ -553,10 +586,7 @@ sub related_resultset { } } - $query = ($query ? { '-and' => [ $cond, $query ] } : $cond); - $rsrc->related_source($rel)->resultset->search( - $query, $attrs - ); + $rsrc->related_source($rel)->resultset->search( $cond, $attrs ); } }; } @@ -636,7 +666,7 @@ sub new_related { return $self->search_related($rel)->new_result( $self->result_source->_resolve_relationship_condition ( infer_values_based_on => $data, rel_name => $rel, - self_resultobj => $self, + self_result_object => $self, foreign_alias => $rel, self_alias => 'me', )->{inferred_values} ); @@ -773,8 +803,8 @@ call set_from_related on the book. This is called internally when you pass existing objects as values to L, or pass an object to a belongs_to accessor. -The columns are only set in the local copy of the object, call L to -set them in the storage. +The columns are only set in the local copy of the object, call +L to update them in the storage. =cut @@ -784,7 +814,7 @@ sub set_from_related { $self->set_columns( $self->result_source->_resolve_relationship_condition ( infer_values_based_on => {}, rel_name => $rel, - foreign_resultobj => $f_obj, + foreign_values => $f_obj, foreign_alias => $rel, self_alias => 'me', )->{inferred_values} ); @@ -944,13 +974,16 @@ Removes the link between the current object and the related object. Note that the related object itself won't be deleted unless you call ->delete() on it. This method just removes the link between the two objects. -=head1 AUTHOR AND CONTRIBUTORS +=head1 FURTHER QUESTIONS? -See L and L in DBIx::Class +Check the list of L. -=head1 LICENSE +=head1 COPYRIGHT AND LICENSE -You may distribute this code under the same terms as Perl itself. +This module is free software L +by the L. You can +redistribute it and/or modify it under the same terms as the +L. =cut