简体   繁体   English

symfony2中可选的多个oneToMany关系

[英]optional multiple oneToMany relation in symfony2

I have entities in Doctrine Symfony2: User, Channel, Video and Comment; 我在Doctrine Symfony2中有实体:用户,频道,视频和评论; user can report one of them. 用户可以报告其中一个。 I designed Report entity with these fields: 我用这些字段设计了Report实体:

  • userId 用户身份
  • status 状态
  • reportTime reportTime
  • description 描述

how can I reference to reported Entity ?? 我怎样才能参考报告的实体? because all reported fields are similar for all entities I want to use just one table for Report and add these fields to Report Entity: 因为我想要仅使用一个表用于报告的所有实体的所有报告字段都相似,并将这些字段添加到报告实体:

  • referenceEntityName(a string and may be one of these: User, Channel, Video, Comment) referenceEntityName(一个字符串,可能是以下之一:User,Channel,Video,Comment)
  • Channel(ManytoOne relation to Channel entity) 频道(与频道实体的ManytoOne关系)
  • Video(ManytoOne relation to Video entity) 视频(ManytoOne与视频实体的关系)
  • Comment(ManytoOne relation to Comment entity) 评论(ManytoOne与评论实体的关系)
  • User(ManytoOne relation to User entity) 用户(与用户实体的ManytoOne关系)

Is this best practice or I should create separate tables for each kind of report ?? 这是最佳实践还是我应该为每种报告创建单独的表?

Edit : based on @Alex answer, I improved Report class and add these methods: 编辑 :基于@Alex答案,我改进了Report类并添加了以下方法:

setEntity($entity){
  if ($obj instanceof Video){
     $this->referenceEntityName = 'Video';
     $this->setVideo();
  }
  elseif($obj instanceof Comment){
     $this->referenceEntityName == 'Comment'
     $this->setComment();
  }
  //...
}

getEntity(){

   if($this->referenceEntityName == 'Video'){
     $this->getVideo()
   }// ifelse statements for other entities ...
}

I till have 4 relation that just one of them is used for each instance, isn't it a bit messy!? 我有4个关系,每个实例只使用其中一个,是不是有点凌乱!? and again is this best practice or I should do something else? 这是最好的做法,还是我应该做点什么呢? what if I want to use FormBuilder class, isn't there any problem?? 如果我想使用FormBuilder类,是不是有什么问题?

In a simple solution, whereby for example you only had Users (and not Videos, Comments and Channels), the solution would be simple; 在一个简单的解决方案中,例如你只有用户(而不是视频,评论和频道),解决方案很简单; each User can have many Reports, and each Report must belong to only one User. 每个用户可以有多个报告,每个报告只能属于一个用户。 This is a one-to-many relationship - one User has many Reports. 这是一对多的关系 - 一个用户有很多报告。 In Symfony 2 and Doctrine, this would be modelled as such: 在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();
    }

    // ...
}

and

// src/Acme/DemoBundle/Entity/Report.php

// ...

class Report
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity="User", inversedBy="reports")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    protected $user;

    // ...

}

In this instance, to create a Report and associate it with a User, we would: 在这种情况下,要创建报告并将其与用户关联,我们将:

// 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 ...

Note, the setUser() method is available because the console command was run to generate them automatically. 请注意,setUser()方法可用,因为运行了控制台命令以自动生成它们。 This is highly recommended as it created the necessary type hinting for you. 强烈建议这样做,因为它为您创建了必要的类型提示。 For pre Symfony 2.5 installations, the command is: 对于Symfony 2.5之前的安装,命令是:

php app/console doctrine:generate:entities Acme

>= 2.5 installations, the command is: > = 2.5安装,命令是:

php bin/console doctrine:generate:entities Acme

Your requirements complicate this simple example somewhat, as Reports can also belong to Comments and Videos etc. For the sake of the example, let's call these things Entities. 您的要求在某种程度上使这个简单示例复杂化,因为报告也可以属于评论和视频等。为了示例,我们将这些事称为实体。 A bad approach would be to simply add 3 new properties to the Report, one for each of the new Entities, and then add 3 new setter methods for the Entities. 一个糟糕的方法是简单地向报表添加3个新属性,每个新实体一个,然后为实体添加3个新的setter方法。 This is bad for 2 reasons: a Report will only ever belong to one of the Entities, and therefore 3 of the properties and setter methods will never be used for each Report entity. 这有两个原因:报告只属于其中一个实体,因此3个属性和setter方法永远不会用于每个Report实体。 Secondly, if you add a new Entity to your business model, or remove one, you need to edit your Report entity, and also the database schema. 其次,如果向业务模型添加新实体或删除实体,则需要编辑报表实体以及数据库架构。

A better method is to simply have one property and set method in your Report, that can be applied to all of your Entities. 更好的方法是在报表中简单地使用一个属性和set方法,这些属性和set方法可以应用于所有实体。 So instead of calling setUser , we could call a setEntity , and have it accept any of the 4. With this approach in mind, let's look back at the first example, and take note of the type hinting in the function signature that would have been produced for the setUser method: 所以我们不是调用setUser ,而是调用setEntity ,让它接受4中的任何一个。考虑到这种方法,让我们回顾第一个例子,并注意函数签名中的类型提示本来是为setUser方法生成:

public function setUser(Acme\DemoBundle\Entity\User $user)

See that it requires to be of type Acme\\DemoBundle\\Entity\\User . 看它需要是Acme\\DemoBundle\\Entity\\User How do we overcome this, and have it accept any of the 4 Entities? 我们如何克服这一点,让它接受4个实体中的任何一个? The solution is to have all Entities be derived from a parent class. 解决方案是让所有实体都从父类派生。 Then make the function type hint at the base class: 然后在基类中创建函数类型提示:

public function setUser(Acme\DemoBundle\Entity\Base $entity)

The base class will contain all common elements, notably a 'name', and as array collection of Reports: 基类将包含所有公共元素,特别是“名称”,以及Reports的数组集合:

// 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();
    }

    // ...
}

and then for each child, for example a User and a Video: 然后为每个孩子,例如用户和视频:

// src/Acme/DemoBundle/Entity/User.php

// ...
use AcmeDemoBundle\Entity\Base;

class User extends Base
{
    /** 
     * @ORM\Column(name="firstname", type="text")
     */
    protected $firstName;

    // ...
}

and the Video 和视频

// src/Acme/DemoBundle/Entity/Video.php

// ...
use AcmeDemoBundle\Entity\Base;

class Video extends Base
{
    /** 
     * @ORM\Column(name="title", type="text")
     */
    protected $title;

    // ...

and change our Report Entity: 并更改我们的报告实体:

// src/Acme/DemoBundle/Entity/Report.php

// ...

class Report
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity="Base", inversedBy="reports")
     * @ORM\JoinColumn(name="base_id", referencedColumnName="id")
     */
    protected $baseEntity;

    // ...

}

Remember to run the doctrine command to generate the setBaseEntity method. 请记住运行doctrine命令以生成setBaseEntity方法。 When you do, notice that it will now accept any class derived of Base 当你这样做时,请注意它现在将接受任何派生的Base类

Then, to put on a Report on a Video for example, we get the Video, create a Report, and add the Video to the Report: 然后,例如,在视频报告中,我们获取视频,创建报告,并将视频添加到报告中:

$video = // get the video you want
$report = new Report();
$report->setBaseEntity($video);

To retrieve all Reports belonging to a Comment, we get the Comment, and get the Reports: 要检索属于评论的所有报告,我们会获得评论,并获取报告:

$video = // get the video you want
$reports = $video->getReports();
foreach($reports as $report){
    $reportText = $report->getText(); // assuming the Report has a `text` field
}

Update: 更新:

The inheritance relationship between these Entities can be modelled in the database with Doctrine using Single Table Inheritance: 这些实体之间的继承关系可以使用Doctrine使用单表继承在数据库中建模:

/**
 * @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"})
 */

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM