Monday, May 07, 2012

Learn it the soft way: OneToMany relationship in Doctrine2 and Symfony2 for dummies

It may seem obvious to a lot, but ...

THE CLASSES

In a forum, there are Authors. These authors can post messages. Every Message has one and only one Author, and an Author can write many Messages : this is typically a OneToMany relationship.
Here are the equivalent classes :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
 * @ORM\Entity
 * @ORM\Table(name="Author")
 * @ORM\Entity(repositoryClass="Acme\DemoBundle\AuthorRepository")
 */
class Author
{
  
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * @var int
     */
    protected $id;
  
    /**
     * @ORM\OneToMany(targetEntity="Message", mappedBy="author", cascade={"persist", "remove"})
     */
    protected $messages;
     
    /**
     * Add message
     *
     * @param Acme\DemoBundle\Entity\Message $message
     */
    public function addMessage(Acme\DemoBundle\Entity\Message $message)
    {
        $this->mesages[] = $message;
        $message->setAuthor($this);
        return $this;
    }
     
    /**
     * Get messages
     *
     * @return Doctrine\Common\Collections\Collection
     */
    public function getMessages()
    {
        return $this->messages;
    }
 
     
    public function __construct()
    {
        $this->messages = new \Doctrine\Common\Collections\ArrayCollection();
    }
}
 
 
/**
 * @ORM\Entity
 * @ORM\Table(name="Message")
 * @ORM\Entity(repositoryClass="Acme\DemoBundle\Repository\MessageRepository")
 */
class Message
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * @var int
     */
    protected $id;
     
    /**
     * @ORM\ManyToOne(targetEntity="Author", inversedBy="messages", cascade={"persist", "update"})
     * @ORM\JoinColumn(name="author_id", referencedColumnName="id", onDelete="cascade", onUpdate="cascade")
     * @var Acme\DemoBundle\Entity\Author
     */
    protected $author;
     
}

THE ISSUE


Now, let's say you want to transfer messages from author 2 to author 1 for some reason. Here is how to do it step by step :
1
2
3
4
5
6
// Get the entities
$Author1 = $authorRepository->find($idAuthor1);
$Author2 = $authorRepository->find($idAuthor2);
 
// Retrieve messages
$messagesOfAuthor2 = $Author2->getMessages();
Here is a visual description of what has happened :

State 0 :
We transfer each of the messages :
1
2
3
4
5
6
7
8
// Move message
foreach($messagesOfAuthor2 as $message)
{
    $Author1->addMessage($message);
    // Leads to state 1
    $Author2->getMessages()->removeElement($message);
    // Leads to state 2
}

State 1 :

State 2 :

Note the deletion of 1 link in the removeElement function, and the addition of 2 links in the addMessage function.
We can now remove the Author 2 and flush the EntityManager :
1
2
3
$entityManager->remove($Author2);
 
$entityManager->flush();


Learn it the soft way: OneToMany relationship in Doctrine2 and Symfony2 for dummies

More than 3 requests, I'll translate this to Chinese.
超过3个请求,我就会把这篇文章翻译成中文。

No comments: