9
votes

i am having trouble deciphering this block of code from doctrine documentation

/** @Entity */
class User
{
    // ...

    /**
     * @ManyToMany(targetEntity="User", mappedBy="myFriends")
     */
    private $friendsWithMe;

    /**
     * @ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
     * @JoinTable(name="friends",
     *      joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="friend_user_id", referencedColumnName="id")}
     *      )
     */
    private $myFriends;

    // ...
}

below is how i decipher a one to many bidirectional relationship

alt text
(source: tumblr.com)

but if i use the same method, ... below is what i get

alt text http://img514.imageshack.us/img514/2918/snagprogram0000.png

UPDATE

i shld clarify my question. basically, i dont understand how is the opposite of myFriends, friendsWithMe. how i shld make sense of this code and more importantly know how to code such relationships myself.

2

2 Answers

12
votes

i give a try at answering my question, i am still quite blur with this, hope someone can really give a better answer,

so 1st to answer the question abt how do i derive with $friendsWithMe

basically, i started off with "decoding" a simpler, more common, many to many bidirectional relationship.

  • 1 user can be in many groups
    • $user->groups
  • 1 group can have many users
    • $group->users

very straight forward. but how does this make sense in SQL?

alt text

code to implement

# select groups user is in
select group_id from users_groups
where user_id = 1

#select users of group
select user_id from users_groups
where group_id = 1

now to the actual model ... in SQL

alt text

in code

# select friends of given user
# $user->myFriends
select friend_id from friends
where user_id = 1;

# select users that are friends of given user
# $user->friendsWithMe
select user_id from friends
where friend_id = 1;

ah ha! select users that are friends of given user. so this is how i get $friendsWithMe. then to fill up the inversedBy & mappedBy & the rest of the class?

1st look at the bottom note.

alt text

not clear without so much and deep thinking, abt 2 days. i guess

then as practice how do i create a many to many self referencing relationship from scratch?

the example i am going to work on is... hmm, quite crappy i think but, i'll try :) ... 1 user/student can have many teachers. 1 teacher can have many users/students. 1 user can be a teacher and student here. u know like in forums such as these, when u answer someones questions, you are a teacher. when u ask, u are a student

the ERD will look like

alt text

some code to select, students of teachers, teachers of students

# select students of teacher
# $teacher->students
select student from teacher_student 
where teacher = 1;

# select teachers of student
# $student->teachers
select teacher from teacher_student
where student = 2;

ok, the doctrine part?

/** @Entity @Table(name="users")) */
class User {
    /**
     * @Id @Column(type="integer")
     * @GeneratedValue(strategy="AUTO")
     */
    private $id;
    /**
     * @Column(type="string", length="30")
     */
    private $name;
    /**
     * @ManyToMany(targetEntity="User", inversedBy="teachers")
     * @JoinTable(name="Teachers_Students",
     *              joinColumns={@JoinColumn(name="teacher", referencedColumnName="id")},
     *              inverseJoinColumns={@JoinColumn(name="student", referencedColumnName="id")}
     *              )
     */
    private $students;
    /**
     * @ManyToMany(targetEntity="User", mappedBy="students")
     */
    private $teachers;
}

which generated this tables for me

# users
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

#teachers_students
CREATE TABLE `teachers_students` (
  `teacher` int(11) NOT NULL,
  `student` int(11) NOT NULL,
  PRIMARY KEY (`teacher`,`student`),
  KEY `student` (`student`),
  CONSTRAINT `teachers_students_ibfk_2` FOREIGN KEY (`student`) REFERENCES `users` (`id`),
  CONSTRAINT `teachers_students_ibfk_1` FOREIGN KEY (`teacher`) REFERENCES `users` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

at last i done it! lets test it ... erm i am getting

Fatal error: Class 'Entities\User' not found in D:\ResourceLibrary\Frameworks\Doctrine\tools\sandbox\index.php on line 61

when i try to do a

$user = new User;

zzz ...

i have also blogged abt this question and my explaination on my tumblr

4
votes

The question is, having the M:N table:

  • friend_user_id
  • user_id

with two users id 1 and 2. Do you have only:

friend_user_id = 1 and user_id = 2

or both

friend_user_id = 1 and user_id = 2 user_id = 2 and friend_user_id = 1

You can implement both ways, depending on how you code the management of the collection of the owning side.

Case A:

public function addFriend(User $friend)
{
    $this->myFriends[] = $friend;
}

Case B:

public function addFriend(User $friend)
{
    $this->myFriends[] = $friend;
    $friend->myFriends[] = $this; // php allows to access private members of objects of the same type
}