[CDBI] Many-to-many relationships of same type?

Ciaran Hamilton class-dbi at theblob.org
Tue Jun 19 14:42:10 BST 2007

Hi there,

I'm a newcomer to Class::DBI, and I'm having a problem - I need to
implement a many-to-many relationship where the objects involved are
both of the same type. The specific application is using data obtained
from LiveJournal, where users can have other users as 'friends'. I
can't seem to get this to work since it appears to use the first
object of this type. This is basically what I have in the relevant

# ============== lib/LJData/User.pm

package LJData::User;
use base qw(LJData::DBI);

__PACKAGE__->columns(All => qw(userid username account_type
last_interests_fetch_date last_friends_fetch_date));

# NOTE: It's up to the code to ensure these are consistent!
__PACKAGE__->has_many(friends   => [ 'LJData::Friends' => 'friend' ]);
# __PACKAGE__->has_many(friendsof => [ 'LJData::Friends' => 'friendof' ]);

__PACKAGE__->has_many(interests => [ 'LJData::UserInterests' => 'interest' ]);


# ============== lib/LJData/Interest.pm

package LJData::Interest;
use base qw(LJData::DBI);

__PACKAGE__->columns(All => qw(intid intcount interest lastupdated));
__PACKAGE__->has_many(users => [ 'LJData::UserInterests' => 'user' ]);


# ============== lib/LJData/Friends.pm

package LJData::Friends;
use base qw(LJData::DBI);

__PACKAGE__->columns(Primary => qw(friendof friend));
__PACKAGE__->has_a(friendof => 'LJData::User');
__PACKAGE__->has_a(friend   => 'LJData::User');


# ============== lib/LJData/UserInterests.pm

package LJData::UserInterests;
use base qw(LJData::DBI);

__PACKAGE__->columns(Primary => qw(user interest));
__PACKAGE__->has_a(user     => 'LJData::User');
__PACKAGE__->has_a(interest => 'LJData::Interest');


# ==============

The user<->interests mapping works fine, but the friend/friendof
mapping doesn't. I commented out the 'friendsof' has_many declaration
in User.pm to see if that was the problem but it isn't.

When running this short example program:

# ==============

use lib 'lib';

use LJData::User;
my $dbh = LJData::User->db_Main;
$dbh->trace(2);   # to enable SQL output

my $user = LJData::User->retrieve(129);
my $i = $user->interests;
my $f = $user->friends;
# ==============

The debug output shows that this SQL is being executed:

# ==============
  (for the retrieve:)
SELECT userid
FROM   users
WHERE  userid = '129'

  (for the interests:)
SELECT user, interest
FROM   user_interests
WHERE  user = '129'

  (for the friends:)
SELECT friendof, friend
FROM   friends
WHERE  friend = '129'
# ==============

The retireve and interests are going through properly but the friends
one should be using 'friendof' in the WHERE clause instead of 'friend'
(since we want to find all the friends of that user). Is this a bug in
Class::DBI when both ends are of the same type, or is it just
something that I'll have to override the 'friends' sub for?


 - Ciaran.

More information about the ClassDBI mailing list