From: Peter Rabbitson Date: Mon, 17 Jan 2011 03:17:44 +0000 (+0100) Subject: Add some extra tests written while debugging, remove design draft X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=c8d7a1d11a50a699f4a59c1b3e76410d10181bde;p=dbsrgits%2FDBIx-Class-Historic.git Add some extra tests written while debugging, remove design draft --- diff --git a/BRANCH b/BRANCH deleted file mode 100644 index 78cb8d5..0000000 --- a/BRANCH +++ /dev/null @@ -1,88 +0,0 @@ -How to allow custom relations: - -* add_relationship is augmented to accept a coderef in addition to the -traditional { foreign... => self... } hashref to specify an ON join -condition. The coderef's signature is: - -my ( $me_alias, $rel_alias, $me_result_source, $rel_name, $optional_me_object ) = @_; - -Normally a condition can be constructed using only the first 2 arguments -(i.e. { "$left_alias.colA" => { '>', \"$right_alias.colB" } } ). - -The next two arguments are supplied in case the coderef wants to do something -clever. Having the result source and the corresponding rel-name (name, not -alias) - one has access to virtually the entire DBIC object structure, -including a raw $dbh (but don't do that) - -The last argument is supplied in cases like $row_obj->custom_relationship -and *may* be used as an optimization path, see discussion of return values -below. - - -The coderef is expected to return one or two values - ($on_as_where, $vals_from_related): - -- an *SQLA* where-clause compatible structure (this implies that - t1.cola = t2.colb must be written as { t1.cola => \'t2.colb' }). - There are plans to introduce an -ident => 'string' SQLAHacks - operator to make the \'column' unnecessary (and allow proper - quoting when needed). The DBIC-SQLA shim is then augmented to - recognize such conditions, and pass them through _recurse_where - in order to produce the final textual ON clause (folding whatever - bind values at the proper places) - -- an OPTIONAL hashref of resolved values, when an $optional_me_object - is supplied to the coderef. This is the data that will be used to - make $some_row_obj->set_from_related ($another_obj) work, and also - to optimise $row_obj->relationship calls, by avoiding a full-join - and instead constructing a WHERE clause with the contents of said - hashref. Naturally the values of this hashref *must be* plain - scalars. - (Note - such rel-to-ident resolution is currently possible for *all* - DBIC relationships, but can not be guaranteed for all custom rels - possible via this syntax. Thus the escape hatch of a *mandatory* - ON clause, which can be used to construct a full-blown JOIN even in - the $obj->rel case - - -Why the complexity of two RVs - custom rels are generally simplifiable -right? - -This is generally true, except when it isn't. The main issue is that a -coderef is a blackbox, and we want to keep it this way for simplicity. -This means that no state is communicated from DBIC to the coderef (except -for the optional row-obj), and no state is communicated out when the -coderef returns (i.e. you can use this hashref for real joins but not for -set_from_related). - -Here are a couple of edge cases when it is crucial to know if we have -a return value for a specific scenario - -- Given a relationship - { 'artist.name' => { '!=', 'bob' }, 'artist.id' => \'cds.artistid' } - we *have* to do a full join when doing $artist->cds, as this is the - only way to evaluate the artist.name condition. For this we need a - defined $on_as_where, but a missing $vals_from_related, which will - signal the need to wrap a full query. Also set_from_related will - throw an exception here, as it largely makes no sense. - -- Given a relationship - { 'cd.year' => '2000', 'artist.id' => \'cds.artistid } - we could use the knowledge of the year to pre-fill the correct value - via $artist->create_related('year_2k_cd', {}); - - -What needs to be adjusted (non-exhaustive summary): - -* $obj->create_related is implemented on top of search_related and - set_from_related. While search_related will always work, the other - may not as discussed above - -* Relationships definitions are treated as fully-introdspectable structures - and multiple codepaths expect _resolve_condition to always return something - akin to $vals_from_related above. A grep for _resolve_condition will - highlight the problematic use-cases - -Additional Note: - -When using extended_rels all cascade options default to 0. diff --git a/t/relationship/custom.t b/t/relationship/custom.t index 68936e3..5d44d18 100644 --- a/t/relationship/custom.t +++ b/t/relationship/custom.t @@ -134,22 +134,21 @@ throws_ok { } qr/\Qunable to set_from_related - no simplified condition available for 'cds_90s'/, 'Create failed - non-simplified rel'; # Do a self-join last-entry search -my @last_track_ids; +my @last_tracks; for my $cd ($schema->resultset('CD')->search ({}, { order_by => 'cdid'})->all) { - push @last_track_ids, $cd->tracks - ->search ({}, { order_by => { -desc => 'position'} }) - ->get_column ('trackid') - ->next; + push @last_tracks, $cd->tracks + ->search ({}, { order_by => { -desc => 'position'} }) + ->next || (); } -my $last_tracks = $schema->resultset('Track')->search ( +my $last_tracks_rs = $schema->resultset('Track')->search ( {'next_track.trackid' => undef}, { join => 'next_track', order_by => 'me.cd' }, ); is_deeply ( - [$last_tracks->get_column ('trackid')->all], - [ grep { $_ } @last_track_ids ], + [$last_tracks_rs->get_column ('trackid')->all], + [ map { $_->trackid } @last_tracks ], 'last group-entry via self-join works', ); @@ -187,4 +186,27 @@ lives_ok { is(scalar @artists, 1, 'only one artist is associated'); +# Make a single for each last_track +my @singles = map { + $_->create_related('cd_single', { + title => $_->title . ' (the single)', + artist => $artist, + year => 1999, + }) } @last_tracks +; + +# See if chaining works +is_deeply ( + [ map { $_->title } $last_tracks_rs->search_related('cd_single')->all ], + [ map { $_->title } @singles ], + 'Retrieved singles in proper order' +); + +# See if prefetch works +is_deeply ( + [ map { $_->cd_single->title } $last_tracks_rs->search({}, { prefetch => 'cd_single' })->all ], + [ map { $_->title } @singles ], + 'Prefetched singles in proper order' +); + done_testing;