在一个简单的解决方案中,例如您只有用户(而不是视频、评论和频道),解决方案会很简单;每个用户可以有多个报表,每个报表必须只属于一个用户。这是一对多的关系——一个用户有多个报告。在 Symfony 2 和 Doctrine 中,这将被建模为:
// src/Acme/DemoBundle/Entity/User.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class User
{
// ...
/**
* @ORM\OneToMany(targetEntity="Report", mappedBy="user")
*/
protected $reports;
public function __construct()
{
$this->reports = new ArrayCollection();
}
// ...
}
和
// src/Acme/DemoBundle/Entity/Report.php
// ...
class Report
{
// ...
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="reports")
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
// ...
}
在这种情况下,要创建报告并将其与用户关联,我们将:
// get the User the Report will belong to
$user = $em->getRepository('AcmeDemoBundle:User')->find(1);
// create the Report
$report = new Report();
// add the User to the Report
$report->setUser($user);
// then persist it, etc ...
注意,setUser() 方法是可用的,因为运行控制台命令来自动生成它们。强烈建议这样做,因为它为您创建了必要的类型提示。对于 Symfony 2.5 之前的安装,命令是:
php app/console doctrine:generate:entities Acme
>= 2.5 安装,命令为:
php bin/console doctrine:generate:entities Acme
您的要求使这个简单的示例有些复杂,因为报告也可以属于评论和视频等。为了示例,我们将这些东西称为实体。一个不好的方法是简单地向报告中添加 3 个新属性,每个新实体一个,然后为实体添加 3 个新的 setter 方法。这很糟糕,原因有两个:一个报告将永远属于一个实体,因此三个属性和设置方法将永远不会用于每个报告实体。其次,如果您在业务模型中添加或删除新实体,则需要编辑报表实体以及数据库架构。
更好的方法是在您的报告中简单地使用一个属性和设置方法,可以将其应用于您的所有实体。因此,我们可以调用setEntity,而不是调用setUser,并让它接受4 个中的任何一个。考虑到这种方法,让我们回顾第一个示例,并注意函数签名中的类型提示为setUser 方法生成的:
public function setUser(Acme\DemoBundle\Entity\User $user)
请注意它必须是Acme\DemoBundle\Entity\User 类型。我们如何克服这一点,并让它接受 4 个实体中的任何一个?解决方案是让所有实体都从父类派生。然后在基类中做函数类型提示:
public function setUser(Acme\DemoBundle\Entity\Base $entity)
基类将包含所有常见元素,特别是“名称”,并作为报告的数组集合:
// src/Acme/DemoBundle/Entity/Base.php
// ...
use Doctrine\Common\Collections\ArrayCollection;
class Base
{
// ...
/**
* @ORM\Column(name="name", type="text")
*/
protected $name
/**
* @ORM\OneToMany(targetEntity="Report", mappedBy="baseEntity")
*/
protected $reports;
public function __construct()
{
$this->reports = new ArrayCollection();
}
// ...
}
然后对于每个孩子,例如一个用户和一个视频:
// src/Acme/DemoBundle/Entity/User.php
// ...
use AcmeDemoBundle\Entity\Base;
class User extends Base
{
/**
* @ORM\Column(name="firstname", type="text")
*/
protected $firstName;
// ...
}
还有视频
// src/Acme/DemoBundle/Entity/Video.php
// ...
use AcmeDemoBundle\Entity\Base;
class Video extends Base
{
/**
* @ORM\Column(name="title", type="text")
*/
protected $title;
// ...
并更改我们的报告实体:
// src/Acme/DemoBundle/Entity/Report.php
// ...
class Report
{
// ...
/**
* @ORM\ManyToOne(targetEntity="Base", inversedBy="reports")
* @ORM\JoinColumn(name="base_id", referencedColumnName="id")
*/
protected $baseEntity;
// ...
}
记得运行学说命令来生成 setBaseEntity 方法。当您这样做时,请注意它现在将接受从 Base 派生的任何类
然后,以对视频进行报告为例,我们获取视频,创建报告,然后将视频添加到报告中:
$video = // get the video you want
$report = new Report();
$report->setBaseEntity($video);
为了检索属于某个评论的所有报告,我们获取评论,并获取报告:
$video = // get the video you want
$reports = $video->getReports();
foreach($reports as $report){
$reportText = $report->getText(); // assuming the Report has a `text` field
}
更新:
这些Entities之间的继承关系可以用Doctrine在数据库中建模,使用Single Table Inheritance:
/**
* @ORM\Entity
* @ORM\Table(name="base_entities")
* @ORM\InheritanceType("SINGLE_TYPE")
* @ORM\Discriminator(name="entity_type", type="string")
* @ORM\DiscriminatorMap({"user" = "User", "comment" = "Comment", "video" = "Video", "channel" = "Channel"})
*/