Home » Php » php – How to ManyToMany and OneToMany in Symfony and Doctrine?

php – How to ManyToMany and OneToMany in Symfony and Doctrine?

Posted by: admin July 12, 2020 Leave a comment

Questions:

I find the documentation very poor when it comes to explaining the creation of relationships between entities. So, i’ll have to ask for help to my fellow StackExchangers. So, i’m trying to build the following cases:

Case 1

A User belongs to one or more Group, and a Group can have many Permission. A User also can have a Permission.

Case 2

A Ticket has a Category, multiple Tag and multiple Comment.

Thanks in advance!

How to&Answers:

Sure thing. First thing to understand is that there is no “one way” to do this. Doctrine gives a lot of flexibility in terms of how you define the relationship – even if multiple definitions produce the exact same DDL (and this is important to understand – some of the mapping choices only effect the object-side of the ORM, not the model-side)

Here’s your Users/Groups/Permissions example, which are actually all many-to-many associations (I excluded all non-relevant but required code, like PK column definition)

<?php

namespace Your\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 */
class User
{
  /**
   * Many-To-Many, Unidirectional
   *
   * @var ArrayCollection $groups
   *
   * @ORM\ManyToMany(targetEntity="Group")
   * @ORM\JoinTable(name="user_has_group",
   *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
   *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
   * )
   */
  protected $groups;

  /**
   * Many-To-Many, Unidirectional
   *
   * @var ArrayCollection $permissions
   *
   * @ORM\ManyToMany(targetEntity="Permission")
   * @ORM\JoinTable(name="user_has_permission",
   *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
   *      inverseJoinColumns={@ORM\JoinColumn(name="permission_id", referencedColumnName="id")}
   * )
   */
  protected $permissions;

  public function __construct()
  {
    $this->groups = new ArrayCollection();
    $this->permissions = new ArrayCollection();
  }
}

/**
 * @ORM\Entity
 */
class Group
{
  /**
   * Many-To-Many, Unidirectional
   *
   * @var ArrayCollection $permissions
   *
   * @ORM\ManyToMany(targetEntity="Permission")
   * @ORM\JoinTable(name="group_has_permission",
   *      joinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")},
   *      inverseJoinColumns={@ORM\JoinColumn(name="permission_id", referencedColumnName="id")}
   * )
   */
  protected $permissions;

  public function __construct()
  {
    $this->permissions = new ArrayCollection();
  }
}

/**
 * @ORM\Entity
 */
class Permission {}

If you have questions about what’s going on here, let me know.

Now, to your second example

<?php

namespace Your\Bundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 */
class Ticket
{
  /**
   * Many-To-One, Unidirectional
   *
   * @var Category
   *
   * @ORM\ManyToOne(targetEntity="Category")
   * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
   */
  protected $category;

  /**
   * Many-To-Many, Unidirectional
   *
   * @var ArrayCollection $permissions
   *
   * @ORM\ManyToMany(targetEntity="Tag")
   * @ORM\JoinTable(name="tickt_has_tag",
   *      joinColumns={@ORM\JoinColumn(name="ticket_id", referencedColumnName="id")},
   *      inverseJoinColumns={@ORM\JoinColumn(name="tag_id", referencedColumnName="id")}
   * )
   */
  protected $tags;

  /**
   * One-To-Many, Bidirectional
   *
   * @var ArrayCollection $comments
   *
   * @ORM\OneToMany(targetEntity="Comment", mappedBy="ticket")
   */
  protected $comments;

  public function __construct()
  {
    $this->tags = new ArrayCollection();
    $this->comments = new ArrayCollection();
  }
}

/**
 * @ORM\Entity
 */
class Comment
{
  /**
   * Many-To-One, Bidirectional
   *
   * @var Ticket $ticket
   *
   * @ORM\ManyToOne(targetEntity="Ticket")
   * @ORM\JoinColumn(name="ticket_id", referencedColumnName="id")
   */
  protected $ticket=null;
}

/**
 * @ORM\Entity
 */
class Tag {}

/**
 * @ORM\Entity
 */
class Category {}

As before, let me know if you want any of this explained.

P.S. None of this was actually tested, I just kinda banged it out in my IDE real fast. There might be a typo or two 😉

Answer:

Try this:

Class User {  
 /**
  * @ORM\OneToMany(targetEntity="path\to\group", mappedBy="user", cascade={"persist", "remove"})
  */
  private $group;

You will have One to Many relation between User and Group.. The targetEntity is the path to the Entity that you want have a relationship with, the mappedBy is the variable from the Group Entity. cascade means User can add to Group and remove from Group

Class Group {

/**
 * @ORM\ManyToOne(targetEntity="path\to\user, inversedBy="group")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
private $user;

This is the reserve side of the relationship.. targetEntity should have the path back to the parent entity, which is User in this case. inversedBy is the variable from the User Entity. JoinColumn is just telling Doctrine what to join on, this is automatically done if you don’t set it yourself.