与一个实体的多个OneToMany关系

时间:2015-10-19 13:49:39

标签: php symfony doctrine-orm

曾经在黑暗的深渊中,有一段时间在Symfony的土地深处,有一个沮丧的程序员。他尝试过并试过但不知何故,邪恶的教义一次又一次地被打击。恶棍JoinsAssociative tablesOne-to-Many/Many-to-One也让他很难受。然后,在一个下午晚些时候StackOverflow,它的社区来救援。

足够的童话故事。我的问题是我有三个表都应该引用同一个表来获取附件。

- Mail
- Order
- Ticket

这三个实体中的每一个都可以有附件。所以我建立了一个附件实体。

现在,我的数据库包含以下内容

Table: mails
- id
- from
- to
- message

Table attachments
- id
- name
- path

Table: orders
- id
- ...

Table: tickets
- id
- name
- description
- ...

Table attachment_associations
- id
- type
- parent_id
- attachment_id

我想做的是能够将订单,门票和邮件映射到相同的附件表。

然而,我坚持如何在学说中这样做。

更新

我尝试使用以下方法。这确实得到了我正在寻找的记录。但我不知道如何使用此方法自动创建,更新或删除关联表(连接表)中的记录。

/**
 * @ORM\ManyToMany(targetEntity="\...\...\Entity\Attachment")
 * @ORM\JoinTable(name="attachment_associations",
 *      joinColumns={@ORM\JoinColumn(name="parentId", referencedColumnName="id")},
 *      inverseJoinColumns={
 *          @ORM\JoinColumn(name="attachmentId", referencedColumnName="id")
 *     }
 * )
 */
protected $attachments;

另一次更新

如果删除邮件,订单或故障单,是否也会删除所有相应的附件?

2 个答案:

答案 0 :(得分:4)

实现此目的的一种方法是实施mapped super class class table inheritance,其中performance implications来自其他实体。

虽然您需要根据具体项目判断{{3}}。

这是一个简单的例子:

映射的超类

<?php

namespace AcmeBundle\Model;

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


/**
 * @ORM\Entity()
 * @ORM\InheritanceType("JOINED")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 */
abstract class SuperClass
{
    /**
     * @var int
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    protected $id;

    /**
     * @var Attachment[]
     *
     * @ORM\ManyToMany(targetEntity="Attachment", mappedBy="parents")
     */
    protected $attachments = [];

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->attachments = new ArrayCollection();
    }

    // put setters/getters for $attachments here
}

和附件管理关联。

<?php

namespace AcmeBundle\Model;

use Doctrine\ORM\Mapping as ORM;


/**
 * @ORM\Entity()
 */
class Attachment
{
    /**
     * @var int
     *
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var SuperClass
     *
     * @ORM\ManyToMany(targetEntity="SuperClass", inversedBy="attachments")
     */
    private $parents;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->parents = new ArrayCollection();
    }
}

实体只是扩展超类

<?php

namespace AcmeBundle\Model;

use Doctrine\ORM\Mapping as ORM;


/**
 * @ORM\Entity()
 */
class Ticket extends SuperClass
{
}

答案 1 :(得分:2)

通过使用带有连接表的单向一对多,可以在一个表中包含所有附件。 In doctrine this is done with a unidirectional Many-To-Many with a unique constraint on the join column。这意味着一个表具有附件,但不同的连接表连接到每个父表。

这种解决方案的缺点是它是单向的,意味着你的附件不知道关系的拥有方。

在完整代码中,这将是这样的:

AttachmentsTrait包含附件的setter和getter以防止代码重复:

<?php

namespace Application\Entity;

use Doctrine\Common\Collections\Collection; 

/**
 * @property Collection $attachments
 */
trait AttachmentTrait
{
    /**
     * Add attachment.
     *
     * @param Attachment $attachment
     * @return self
     */
    public function addAttachment(Attachment $attachment)
    {
        $this->attachments[] = $attachment;

        return $this;
    }

    /**
     * Add attachments.
     *
     * @param Collection $attachments
     * @return self
     */
    public function addAttachments(Collection $attachments)
    {
        foreach ($attachments as $attachment) {
            $this->addAttachment($attachment);
        }
        return $this;
    }

    /**
     * Remove attachment.
     *
     * @param Attachment $attachments
     */
    public function removeAttachment(Attachment $attachment)
    {
        $this->attachments->removeElement($attachment);
    }

    /**
     * Remove attachments.
     *
     * @param Collection $attachments
     * @return self
     */
    public function removeAttachments(Collection $attachments)
    {
        foreach ($attachments as $attachment) {
            $this->removeAttachment($attachment);
        }
        return $this;
    }

    /**
     * Get attachments.
     *
     * @return Collection
     */
    public function getAttachments()
    {
        return $this->attachments;
    }
}

您的Mail实体:

<?php

namespace Application\Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;    

class Mail
{
    use AttachmentsTrait;

    /**
     * @var integer
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Attachment")
     * @ORM\JoinTable(name="mail_attachments",
     *     inverseJoinColumns={@ORM\JoinColumn(name="attachment_id", referencedColumnName="id")},
     *     joinColumns={@ORM\JoinColumn(name="mail_id", referencedColumnName="id", unique=true)}
     * )
     */
    $attachments;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->attachments = new ArrayCollection();
    }
}

您的Order实体:

<?php

namespace Application\Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;    

class Order
{
    use AttachmentsTrait;

    /**
     * @var integer
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Attachment")
     * @ORM\JoinTable(name="order_attachment",
     *     inverseJoinColumns={@ORM\JoinColumn(name="attachment_id", referencedColumnName="id")},
     *     joinColumns={@ORM\JoinColumn(name="order_id", referencedColumnName="id", unique=true)}
     * )
     */
    $attachments;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->attachments = new ArrayCollection();
    }
}

您的Ticket实体:

<?php

namespace Application\Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;    

class Ticket
{
    use AttachmentsTrait;

    /**
     * @var integer
     * @ORM\Id
     * @ORM\Column(type="integer", nullable=false)
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Attachment")
     * @ORM\JoinTable(name="ticket_attachment",
     *     inverseJoinColumns={@ORM\JoinColumn(name="attachment_id", referencedColumnName="id")},
     *     joinColumns={@ORM\JoinColumn(name="ticket_id", referencedColumnName="id", unique=true)}
     * )
     */
    $attachments;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->attachments= new ArrayCollection();
    }
}

编辑:

如果您真的希望Attachment知道另一方,您可以在其间添加一个额外的实体来管理它。这意味着将连接表本身设置为实体,例如:MailAttachmentTicketAttachmentOrderAttachment