如何在Symfony和Doctrine中使用多对多和一对多关系?

8
我发现文档在解释实体之间的关系创建方面十分简略。因此,我需要向我的StackExchanger同行请求帮助。我正在尝试构建以下情况: 情况1 一个User属于一个或多个Group,而一个Group可以有许多Permission。一个User也可以有一个Permission情况2 一个Ticket有一个Category,多个Tag和多个Comment
提前感谢!
2个回答

17
当然,首先要明白的是,并没有一种“正确的方法”来完成这个任务。Doctrine提供了很多灵活性,可以让您定义关系,即使多个定义产生相同的DDL(这很重要,因为某些映射选择只影响ORM的对象端,而不是模型端)。
以下是您的用户/组/权限示例,它们实际上都是多对多关联(我排除了所有无关但必需的代码,如PK列定义)。
<?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 {}

如果您对这里正在发生的事情有疑问,请让我知道。现在,进入您的第二个例子。
<?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 {}

和之前一样,如果您需要任何解释,请告诉我。

附言:这些内容都没有经过测试,我只是在我的IDE中快速编写了它们。可能会有一两个拼写错误 ;)


另外,为什么要使用不同的表名?权限ID不应该声明吗? - vinnylinux
1
@vinnylinux - 我不完全理解你的问题。inverseJoinColumns只是用来声明单向多对多关系的方式 - 没有什么“为什么”可以回答 - 这就是它的实现方式。而且我完全不明白你的第二个问题在问什么。 - Peter Bailey
嘿@vinnylinux,抱歉让你等了这么久。我度假去了几周。你想看哪个示例的getter/setter?#1还是#2? - Peter Bailey
谢谢您的回复,但我已经让Doctrine生成它们了。我不知道它能够这样做。无论如何,还是谢谢!:D - vinnylinux
TicketCategory 缺少什么吗?也许是 inversedby 吗?因为它似乎不起作用!我得到一个异常:表单的视图数据应该是标量、数组或类 Proxies__CG__\Acme\DemoBundle\Entity\TicketCategory 的实例,但却是一个 Proxies__CG__\Acme\DemoBundle\Entity\TicketCategory 类的实例。你可以通过将 "data_class" 选项设置为 "Proxies__CG__\Acme\DemoBundle\Entity\TicketCategory" 或添加一个视图转换器来避免此错误,该转换器将 Proxies__CG__\Acme\DemoBundle\Entity\TicketCategory 类的实例转换为标量、数组或 \ArrayAccess 的实例。 - vinnylinux
你好,我正在寻找oneToMany的示例,而在谷歌上的第一个结果是这个问题。所以我可以请你为像我这样的人添加一个快速示例(或者链接)来演示oneToMany吗?还有其他步骤需要完成它的工作(例如使用php app/console doctrine:xxxx命令行),或者它可以“即时运行”吗?非常感谢您详细的回答。 - Olivier Pons

4

试试这个:

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

您将拥有一个“一对多”的关系,其中一个User可以与多个Group建立联系。其中,targetEntity是指要与之建立关系的实体类路径,而mappedBy是来自Group实体类的变量。 cascade表示User可以添加到Group并从Group中删除。

Group类:

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

这是关系的反向。在这种情况下,targetEntity应该返回到父实体的路径,即UserinversedByUser实体中的变量。JoinColumn只是告诉Doctrine要连接什么,如果您不自己设置,则会自动执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接