views:

576

answers:

1

I wish to persist some extra data in a many-to-many relationship by having some extra fields in the association table. For instance, I would like to keep track of what role a user has in a network, such as 'member', 'moderator', 'admin' etc. I would also like to keep track of when he/she joined the network. Now, what I am looking for is an efficient way to retrieve these extra fields using Doctrine. A typical, though very simplified, DQL-query might look like so:

// find.network.by.slug
Doctrine_Query::create()
    ->select('*')
    ->from('Network n')
    ->leftJoin('n.Members u')
    ->where('n.slug = ?');
  1. So, given that I have a User from the Members relation, is there any way I can "backtrack" into the NetworkMembers relation and fetch the extra fields?

  2. If not, how do I re-arrange my schema to enable me to do this efficiently?

    (i.e. I don't want to iterate over all networks the member has to find the network I started from and fetch the role and member_since field).

Code illustration of my problem:

$networkTable = Doctrine::getTable('Network');
$network = $networkTable->executeOne('find.network.by.slug', $slug);
$members = $network->Members;
foreach($members as $member) {
   // How do I access the fields in the NetworksMember association?
}

Schema below:


User:
  tableName: users
  columns:
    user_id:
      name: user_id as userId
      type: integer(8)
      unsigned: 1
      primary: true
      autoincrement: true
    username:
      type: string(30)
      notnull: true
      unique: true
  relations:
    Networks:
      class: Network
      local: user_id
      foreign: network_id
      refClass: NetworkMembers
      type: many


Network:
  tableName: networks
  actAs:
    Sluggable:
      unique: true
      fields: [name]
      canUpdate: true
    Timestampable:
  columns:
    id:
      type: integer(8)
      unsigned: 1
      primary: true
      autoincrement: true
    name:
      type: string(64)
      notnull: true
  relations:
    Members:
      class: User
      type: many
      refClass: NetworkMembers
      local: network_id
      foreign: user_id
      foreignAlias: Networks
      foreignType: many


NetworkMembers:
  actAs:
    Timestampable:
      created:
        name: member_since as memberSince
        type: timestamp
      updated:
        disabled: true
  columns:
    networkId:
      name: network_id as networkId
      type: integer(8)
      unsigned: 1
      primary: true
    userId:
      name: user_id as userId
      type: integer(8)
      unsigned: 1
      primary: true
    role:
      type: enum
      values: [member, moderator, admin]
      default: member
  relations:
    Network:
      type: one
      local: network_id
      foreign: id
    Member:
      class: User
      type: one
      local: user_id
      foreign: user_id


+2  A: 

I got it myself, just had to take a slight different approach:

// find.network.by.slug
        Doctrine_Query::create()
         ->select('*')
         ->from('Network n')
         ->leftJoin('n.NetworkMembers nm')
         ->leftJoin('nm.Member u')
         ->leftJoin('n.Founder f')
         ->leftJoin('n.Icon icon')
         ->where('n.slug = ?')

// later:
$members = $this->network->NetworkMembers;
foreach($members as $networkMember) {
   echo $networkMember->Member->username;
   echo $networkMember->role; // Here I can access the 
                              // NetworkMember association directly
}
PatrikAkerstrand
Did you have to make changes on your schema, or was n.NetworkMembers available rightaway?
Silvan Mühlemann
It's available right away, due to the refClass attribute in the relation.
PatrikAkerstrand