add_relationship, relationship_info, relationships moved to ResultSource
Matt S Trout [Sat, 14 Jan 2006 03:18:27 +0000 (03:18 +0000)]
14 files changed:
lib/DBIx/Class/DB.pm
lib/DBIx/Class/Relationship.pm
lib/DBIx/Class/Relationship/Base.pm
lib/DBIx/Class/ResultSet.pm
lib/DBIx/Class/ResultSource.pm
lib/DBIx/Class/Schema.pm
t/cdbi-sweet-t/08pager.t
t/cdbi-t/01-columns.t
t/lib/DBICTest.pm
t/lib/DBICTest/BasicRels.pm
t/lib/DBICTest/HelperRels.pm
t/lib/DBICTest/Schema/BasicRels.pm
t/run/06relationship.tl
t/run/16joins.tl

index f65375d..3166c98 100644 (file)
@@ -105,9 +105,13 @@ Rolls back the current transaction.
 
 sub txn_rollback { $_[0]->storage->txn_rollback }
 
-sub resolve_class {
-  warn "resolve_class deprecated as of 0.04999_02";
-  return shift->class_resolver->class(@_);
+{
+  my $warn;
+
+  sub resolve_class {
+    warn "resolve_class deprecated as of 0.04999_02" unless $warn++;
+    return shift->class_resolver->class(@_);
+  }
 }
 
 1;
index 71c0004..cdb3581 100644 (file)
@@ -13,23 +13,6 @@ __PACKAGE__->load_own_components(qw/
   Base
 /);
 
-__PACKAGE__->mk_classdata('_relationships', { } );
-
-sub relationships
-{
-    my $self = shift;
-
-    return keys %{$self->_relationships};
-}
-
-sub relationship_info
-{
-    my $self = shift;
-    my ($rel) = @_;
-
-    return $self->_relationships->{$rel};
-}
-
 =head1 NAME 
 
 DBIx::Class::Relationship - Inter-table relationships
index ce7555b..9a979b8 100644 (file)
@@ -72,28 +72,15 @@ created, which calls C<create_related> for the relationship.
 =cut
 
 sub add_relationship {
-  my ($class, $rel, $f_class, $cond, $attrs) = @_;
-  die "Can't create relationship without join condition" unless $cond;
-  $attrs ||= {};
-  eval "require $f_class;";
-  if ($@) {
-    $class->throw($@) unless $@ =~ /Can't locate/;
-  }
-  my %rels = %{ $class->_relationships };
-  $rels{$rel} = { class => $f_class,
-                  cond  => $cond,
-                  attrs => $attrs };
-  $class->_relationships(\%rels);
-
-  return unless eval { $f_class->can('columns'); }; # Foreign class not loaded
-  eval { $class->_resolve_join($rel, 'me') };
-
-  if ($@) { # If the resolve failed, back out and re-throw the error
-    delete $rels{$rel}; # 
-    $class->_relationships(\%rels);
-    $class->throw("Error creating relationship $rel: $@");
-  }
-  1;
+  shift->result_source->add_relationship(@_);
+}
+
+sub relationships {
+  shift->result_source->relationships(@_);
+}
+
+sub relationship_info {
+  shift->result_source->relationship_info(@_);
 }
 
 sub _resolve_join {
@@ -108,6 +95,7 @@ sub _resolve_join {
     $class->throw("No idea how to resolve join reftype ".ref $join);
   } else {
     my $rel_obj = $class->relationship_info($join);
+    #use Data::Dumper; warn Dumper($class->result_source) unless $rel_obj;
     $class->throw("No such relationship ${join}") unless $rel_obj;
     my $j_class = $rel_obj->{class};
     my %join = (_action => 'join',
index 3d514cc..a33e22f 100644 (file)
@@ -59,11 +59,11 @@ sub new {
         $seen{$j} = 1;
       }
     }
-    push(@{$attrs->{from}}, $source->result_class->_resolve_join($join, $attrs->{alias}));
+    push(@{$attrs->{from}}, $source->resolve_join($join, $attrs->{alias}));
   }
   $attrs->{group_by} ||= $attrs->{select} if delete $attrs->{distinct};
   foreach my $pre (@{delete $attrs->{prefetch} || []}) {
-    push(@{$attrs->{from}}, $source->result_class->_resolve_join($pre, $attrs->{alias}))
+    push(@{$attrs->{from}}, $source->resolve_join($pre, $attrs->{alias}))
       unless $seen{$pre};
     my @pre = 
       map { "$pre.$_" }
index c122ddb..335eb5c 100644 (file)
@@ -11,7 +11,7 @@ use base qw/DBIx::Class/;
 __PACKAGE__->load_components(qw/AccessorGroup/);
 
 __PACKAGE__->mk_group_accessors('simple' =>
-  qw/_ordered_columns _columns _primaries name resultset_class result_class schema from/);
+  qw/_ordered_columns _columns _primaries name resultset_class result_class schema from _relationships/);
 
 =head1 NAME 
 
@@ -35,6 +35,7 @@ sub new {
   $new->{resultset_class} ||= 'DBIx::Class::ResultSet';
   $new->{_ordered_columns} ||= [];
   $new->{_columns} ||= {};
+  $new->{_relationships} ||= {};
   $new->{name} ||= "!!NAME NOT SET!!";
   return $new;
 }
@@ -172,6 +173,122 @@ Returns the storage handle for the current schema
 
 sub storage { shift->schema->storage; }
 
+=head2 add_relationship
+
+  $source->add_relationship('relname', 'related_source', $cond, $attrs);
+
+The relation name can be arbitrary, but must be unique for each relationship
+attached to this result source. 'related_source' should be the name with
+which the related result source was registered with the current schema
+(for simple schemas this is usally either Some::Namespace::Foo or just Foo)
+
+The condition needs to be an SQL::Abstract-style representation of the join
+between the tables. For example, if you're creating a rel from Foo to Bar,
+
+  { 'foreign.foo_id' => 'self.id' }                                             
+                                                                                
+will result in the JOIN clause                                                  
+                                                                                
+  foo me JOIN bar bar ON bar.foo_id = me.id                                     
+                                                                                
+You can specify as many foreign => self mappings as necessary.
+
+Valid attributes are as follows:                                                
+                                                                                
+=over 4                                                                         
+                                                                                
+=item join_type                                                                 
+                                                                                
+Explicitly specifies the type of join to use in the relationship. Any SQL       
+join type is valid, e.g. C<LEFT> or C<RIGHT>. It will be placed in the SQL      
+command immediately before C<JOIN>.                                             
+                                                                                
+=item proxy                                                                     
+                                                                                
+An arrayref containing a list of accessors in the foreign class to proxy in     
+the main class. If, for example, you do the following:                          
+                                                                                
+  __PACKAGE__->might_have(bar => 'Bar', undef, { proxy => qw[/ margle /] });    
+                                                                                
+Then, assuming Bar has an accessor named margle, you can do:                    
+                                                                                
+  my $obj = Foo->find(1);                                                       
+  $obj->margle(10); # set margle; Bar object is created if it doesn't exist     
+                                                                                
+=item accessor                                                                  
+                                                                                
+Specifies the type of accessor that should be created for the relationship.     
+Valid values are C<single> (for when there is only a single related object),    
+C<multi> (when there can be many), and C<filter> (for when there is a single    
+related object, but you also want the relationship accessor to double as        
+a column accessor). For C<multi> accessors, an add_to_* method is also          
+created, which calls C<create_related> for the relationship.                    
+                                                                                
+=back
+
+=cut
+
+sub add_relationship {
+  my ($self, $rel, $f_source_name, $cond, $attrs) = @_;
+  die "Can't create relationship without join condition" unless $cond;
+  $attrs ||= {};
+  my %rels = %{ $self->_relationships };
+  $rels{$rel} = { class => $f_source_name,
+                  cond  => $cond,
+                  attrs => $attrs };
+  $self->_relationships(\%rels);
+
+  my $f_source = $self->schema->source($f_source_name);
+  unless ($f_source) {
+    eval "require $f_source_name;";
+    if ($@) {
+      die $@ unless $@ =~ /Can't locate/;
+    }
+    $f_source = $f_source_name->result_source;
+  }
+  return unless $f_source; # Can't test rel without f_source
+
+  eval { $self->resolve_join($rel, 'me') };
+
+  if ($@) { # If the resolve failed, back out and re-throw the error
+    delete $rels{$rel}; # 
+    $self->_relationships(\%rels);
+    die "Error creating relationship $rel: $@";
+  }
+  1;
+}
+
+=head2 relationships()
+
+Returns all valid relationship names for this source
+
+=cut
+
+sub relationships {
+  return keys %{shift->_relationships};
+}
+
+=head2 relationship_info($relname)
+
+Returns the relationship information for the specified relationship name
+
+=cut
+
+sub relationship_info {
+  my ($self, $rel) = @_;
+  return $self->_relationships->{$rel};
+} 
+
+=head2 resolve_join($relation)
+
+Returns the join structure required for the related result source
+
+=cut
+
+sub resolve_join {
+  shift->result_class->_resolve_join(@_);
+}
+
 1;
 
 =head1 AUTHORS
index 678fe5e..0c0ceb8 100644 (file)
@@ -109,7 +109,8 @@ Returns the result source object for the registered name
 
 sub source {
   my ($self, $class) = @_;
-  return $self->class_registrations->{$class}->result_source;
+  return $self->class_registrations->{$class}->result_source
+    if $self->class_registrations->{$class};
 }
 
 =head2 resultset
@@ -181,6 +182,7 @@ sub load_classes {
         die $@ unless $@ =~ /Can't locate/;
       }
       $class->register_class($comp => $comp_class);
+      #$class->register_class($comp_class => $comp_class);
     }
   }
 }
index ab5211a..ea77d81 100644 (file)
@@ -10,7 +10,7 @@ plan tests => 10;
 
 use lib 't/lib';
 
-use_ok('DBICTest');
+use_ok('DBICTest::HelperRels');
 
 DBICTest::CD->load_components(qw/CDBICompat::Pager/);
 
index faaf713..09cae07 100644 (file)
@@ -7,8 +7,7 @@ use Test::More tests => 24;
 #-----------------------------------------------------------------------
 package State;
 
-use base 'DBIx::Class';
-State->load_components(qw/CDBICompat Core/);
+use base 'DBIx::Class::Test::SQLite';
 
 State->table('State');
 State->columns(Essential => qw/Abbreviation Name/);
@@ -34,8 +33,7 @@ sub Snowfall { 1 }
 
 package City;
 
-use base 'DBIx::Class';
-City->load_components(qw/CDBICompat Core/);
+use base 'DBIx::Class::Test::SQLite';
 
 City->table('City');
 City->columns(All => qw/Name State Population/);
@@ -44,8 +42,7 @@ City->has_a(State => 'State');
 
 #-------------------------------------------------------------------------
 package CD;
-use base 'DBIx::Class';
-CD->load_components(qw/CDBICompat Core/);
+use base 'DBIx::Class::Test::SQLite';
 
 CD->table('CD');
 CD->columns('All' => qw/artist title length/);
index bcca718..0afc604 100755 (executable)
@@ -1,131 +1 @@
-use strict;
-use warnings;
-use DBICTest::Schema;
-
-my $db_file = "t/var/DBIxClass.db";
-
-unlink($db_file) if -e $db_file;
-unlink($db_file . "-journal") if -e $db_file . "-journal";
-mkdir("t/var") unless -d "t/var";
-
-my $dsn = "dbi:SQLite:${db_file}";
-
-my $schema = DBICTest::Schema->compose_connection('DBICTest' => $dsn);
-
-my $dbh = DBI->connect($dsn);
-
-my $sql = <<EOSQL;
-CREATE TABLE artist (artistid INTEGER NOT NULL PRIMARY KEY, name VARCHAR);
-
-CREATE TABLE cd (cdid INTEGER NOT NULL PRIMARY KEY, artist INTEGER NOT NULL,
-                     title VARCHAR, year VARCHAR);
-
-CREATE TABLE liner_notes (liner_id INTEGER NOT NULL PRIMARY KEY, notes VARCHAR);
-
-CREATE TABLE track (trackid INTEGER NOT NULL PRIMARY KEY, cd INTEGER NOT NULL,
-                       position INTEGER NOT NULL, title VARCHAR);
-
-CREATE TABLE tags (tagid INTEGER NOT NULL PRIMARY KEY, cd INTEGER NOT NULL,
-                      tag VARCHAR);
-
-CREATE TABLE twokeys (artist INTEGER NOT NULL, cd INTEGER NOT NULL,
-                      PRIMARY KEY (artist, cd) );
-
-CREATE TABLE fourkeys (foo INTEGER NOT NULL, bar INTEGER NOT NULL,
-                      hello INTEGER NOT NULL, goodbye INTEGER NOT NULL,
-                      PRIMARY KEY (foo, bar, hello, goodbye) );
-
-CREATE TABLE onekey (id INTEGER NOT NULL PRIMARY KEY,
-                      artist INTEGER NOT NULL, cd INTEGER NOT NULL );
-
-CREATE TABLE self_ref (id INTEGER NOT NULL PRIMARY KEY,
-                      name VARCHAR );
-
-CREATE TABLE self_ref_alias (self_ref INTEGER NOT NULL, alias INTEGER NOT NULL,
-                      PRIMARY KEY( self_ref, alias ) );
-
-CREATE TABLE producer (producerid INTEGER NOT NULL PRIMARY KEY, name VARCHAR);
-
-CREATE TABLE cd_to_producer (cd INTEGER NOT NULL, producer INTEGER NOT NULL);
-
-INSERT INTO artist (artistid, name) VALUES (1, 'Caterwauler McCrae');
-
-INSERT INTO artist (artistid, name) VALUES (2, 'Random Boy Band');
-
-INSERT INTO artist (artistid, name) VALUES (3, 'We Are Goth');
-
-INSERT INTO cd (cdid, artist, title, year)
-    VALUES (1, 1, "Spoonful of bees", 1999);
-
-INSERT INTO cd (cdid, artist, title, year)
-    VALUES (2, 1, "Forkful of bees", 2001);
-
-INSERT INTO cd (cdid, artist, title, year)
-    VALUES (3, 1, "Caterwaulin' Blues", 1997);
-
-INSERT INTO cd (cdid, artist, title, year)
-    VALUES (4, 2, "Generic Manufactured Singles", 2001);
-
-INSERT INTO cd (cdid, artist, title, year)
-    VALUES (5, 3, "Come Be Depressed With Us", 1998);
-
-INSERT INTO liner_notes (liner_id, notes)
-    VALUES (2, "Buy Whiskey!");
-
-INSERT INTO liner_notes (liner_id, notes)
-    VALUES (4, "Buy Merch!");
-
-INSERT INTO liner_notes (liner_id, notes)
-    VALUES (5, "Kill Yourself!");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (1, 1, "Blue");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (2, 2, "Blue");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (3, 3, "Blue");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (4, 5, "Blue");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (5, 2, "Cheesy");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (6, 4, "Cheesy");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (7, 5, "Cheesy");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (8, 2, "Shiny");
-
-INSERT INTO tags (tagid, cd, tag) VALUES (9, 4, "Shiny");
-
-INSERT INTO twokeys (artist, cd) VALUES (1, 1);
-
-INSERT INTO twokeys (artist, cd) VALUES (1, 2);
-
-INSERT INTO twokeys (artist, cd) VALUES (2, 2);
-
-INSERT INTO fourkeys (foo, bar, hello, goodbye) VALUES (1, 2, 3, 4);
-
-INSERT INTO fourkeys (foo, bar, hello, goodbye) VALUES (5, 4, 3, 6);
-
-INSERT INTO onekey (id, artist, cd) VALUES (1, 1, 1);
-
-INSERT INTO onekey (id, artist, cd) VALUES (2, 1, 2);
-
-INSERT INTO onekey (id, artist, cd) VALUES (3, 2, 2);
-
-INSERT INTO self_ref (id, name) VALUES (1, 'First');
-
-INSERT INTO self_ref (id, name) VALUES (2, 'Second');
-
-INSERT INTO self_ref_alias (self_ref, alias) VALUES (1, 2);
-
-INSERT INTO producer (producerid, name) VALUES (1, 'Matt S Trout');
-
-INSERT INTO cd_to_producer (cd, producer) VALUES (1, 1);
-
-EOSQL
-
-$dbh->do($_) for split(/\n\n/, $sql);
-
-$schema->storage->dbh->do("PRAGMA synchronous = OFF");
-
 1;
index c98c3ea..9ee0938 100644 (file)
@@ -2,5 +2,6 @@ package DBICTest::BasicRels;
 
 use DBICTest::Schema;
 use DBICTest::Schema::BasicRels;
+use DBICTest::Setup;
 
 1;
index 7887bd5..2dec167 100644 (file)
@@ -2,5 +2,6 @@ package DBICTest::HelperRels;
 
 use DBICTest::Schema;
 use DBICTest::Schema::HelperRels;
+use DBICTest::Setup;
 
 1;
index 13948af..0d01ff9 100644 (file)
@@ -19,6 +19,7 @@ DBICTest::Schema::Artist->add_relationship(
 DBICTest::Schema::CD->add_relationship(
     artist => 'DBICTest::Schema::Artist',
     { 'foreign.artistid' => 'self.artist' },
+    { accessor => 'filter' },
 );
 DBICTest::Schema::CD->add_relationship(
     tracks => 'DBICTest::Schema::Track',
@@ -34,7 +35,7 @@ DBICTest::Schema::CD->add_relationship(
 DBICTest::Schema::CD->add_relationship(
     liner_notes => 'DBICTest::Schema::LinerNotes',
     { 'foreign.liner_id' => 'self.cdid' },
-    { join_type => 'LEFT' }
+    { join_type => 'LEFT', accessor => 'single' }
 );
 DBICTest::Schema::CD->add_relationship(
     cd_to_producer => 'DBICTest::Schema::CD_to_Producer',
index ab35be0..0d691a0 100644 (file)
@@ -93,7 +93,7 @@ SKIP: {
 # try to add a bogus relationship using the wrong cols
 eval {
     DBICTest::Schema::Artist->add_relationship(
-        tracks => 'DBICTest::Track',
+        tracks => 'DBICTest::Schema::Track',
         { 'foreign.cd' => 'self.cdid' }
     );
 };
index 20a6214..cab0a93 100644 (file)
@@ -96,17 +96,6 @@ cmp_ok( $rs->count, '==', 1, "Single record in resultset");
 
 is($rs->first->name, 'We Are Goth', 'Correct record returned');
 
-DBICTest::Schema::CD->add_relationship(
-    artist => 'DBICTest::Schema::Artist',
-    { 'foreign.artistid' => 'self.artist' },
-    { accessor => 'filter' },
-);
-
-DBICTest::Schema::CD->add_relationship(
-    liner_notes => 'DBICTest::Schema::LinerNotes',
-    { 'foreign.liner_id' => 'self.cdid' },
-    { join_type => 'LEFT', accessor => 'single' });
-
 $rs = DBICTest->class("CD")->search(
            { 'artist.name' => 'Caterwauler McCrae' },
            { prefetch => [ qw/artist liner_notes/ ],