Symfony Association将OneToOne和OneToMany映射到同一实体

时间:2014-05-06 23:44:36

标签: php symfony doctrine-orm

我有一个View实体代表主页面记录,然后我有一个名为ViewVersion的关联实体,它存储了实体的多个版本,因为它随着时间的推移而发生变化。 View实体设置当前"已发布" ViewVersion字段中的VersionId。这使得简单的OneToOne关联成为可能。但在某些情况下,我还希望获得与此View实体相关联的所有版本,例如如果我想允许用户查看旧版本并还原。所以我需要另一个映射,它是OneToMany。第一个viewVersion将映射到活动的"已发布的"版本,第二个viewVersions将显示所有版本。

实体定义

/**
* @ORM\Entity
* @ORM\Table(name="view")
* @ORM\Entity(repositoryClass="Gutensite\CmsBundle\Entity\View\ViewRepository")
*/
class View extends Entity\Base {

    /**
    * @ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", inversedBy="view", cascade={"persist", "remove"}, orphanRemoval=true)
    * @ORM\JoinColumn(name="versionId", referencedColumnName="id")
    */
    protected $viewVersion;

    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    protected $versionId = NULL;

    /**
 * @ORM\OneToMany(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", mappedBy="viewAll", cascade={"persist", "remove"}, orphanRemoval=true)
 */
protected $viewVersions;
}

/**
* @ORM\Entity
* @ORM\Table(name="view_version")
* @ORM\Entity(repositoryClass="Gutensite\CmsBundle\Entity\View\ViewVersionRepository")
*/
class ViewVersion extends Entity\Base {

    /**
    * @ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\View", mappedBy="viewVersion", cascade={"persist"})
    */
    protected $view;

    /**
    * @ORM\ManyToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\View", inversedBy="viewVersions")
    * @ORM\JoinColumn(name="viewId", referencedColumnName="id")
    */
    protected $viewAll;

    /**
    * The primary view entity that this version belongs to.
    * @ORM\Column(type="integer", nullable=true)
    */
    protected $viewId;

}

这"工作"但是建议与这样的同一个实体有两个关联吗?或者这是一个非常糟糕的主意?

ViewVersion实体将在两种情况下引用单个View实体,但映射的关联需要两个单独的变量,例如ViewViewAll。我不完全确定内部如何为关联工作,以及如何使用带有映射的引用变量。

或者,我可以摆脱OneToOne关联,只需设置一个ViewRepository函数即可获得基于versionId的当前发布版本(就像用于处理getVersion()的旧映射实体一样)。这样做会有效,但是内部开销会更多,因为它会产生两个查询......或者Doctrine会非常聪明地对其进行优化,就像使用getVersion()一样。

注意: 这些other answers不完整。

参考文献: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html http://doctrine-orm.readthedocs.org/en/2.0.x/reference/association-mapping.html#one-to-many-bidirectional

2 个答案:

答案 0 :(得分:0)

通常,我发现最好的方法是以不同的方式解决这个问题。

我之前见过的一个常见模式是您使用单个表来保存所有记录,并且具有“活动”标记。

如果您选择活动的查询是这样的:

SELECT * FROM table WHERE active = true ORDER BY updated_at DESC LIMIT 1;

然后启用一个新的变得如此简单:

UPDATE table SET active = 1, updated_at = '<timestamp>' WHERE id = <new id>;
UPDATE table SET active = 0, updated_at = '<timestamp>' WHERE id = <old id>;

一旦第一个查询命中,您的新页面就会处于活动状态,而您的第二个查询将避免任何形式的怪异,因为该行已经不再有效。

如果你有其他模型依赖于一致的ID来引用,那么另一个保持一定理智的路径就是有一个表用于活动条目(整体而不是部分),然后是第二个附加表用于跟踪版本的元数据。

后一种方法可以通过Doctrine的继承系统(http://docs.doctrine-project.org/en/2.0.x/reference/inheritance-mapping.html)很好地处理,它可以让你定义基本的View类,然后对于“ViewRevision”模型,扩展View并添加一个“Revised on”类型时间戳。

答案 1 :(得分:0)

根据@jmather的建议我已经确定这个模型是“好的”,因为我需要一个其他实体可以访问的单个View实体(例如指向单个View的路由URL,即“page”)。

我已将View的OneToOne关系更改为仅单向,因为ViewVersion已经通过其他OneToMany与View返回关联(因此它不需要两条路径)。

这允许我为$ view-&gt; getPublished()保留一个简单的方法,并且看起来更合乎逻辑。

/**
* @ORM\Entity
* @ORM\Table(name="view")
*/
class View extends Entity\Base {

    /**
    * This is a OneToOne Unidirectional association, just so that we can get the
    * current published version easily, based on the publishedId.
    * @ORM\OneToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\TestVersion")
    * @ORM\JoinColumn(name="publishedId", referencedColumnName="id")
    */
    protected $published;

    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    protected $publishedId = NULL;

    /**
     * This is the regular OneToMany Bi-Directional Association, for all the versions.
 * @ORM\OneToMany(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", mappedBy="view", cascade={"persist", "remove"}, orphanRemoval=true)
 */
protected $versions;
}

/**
* @ORM\Entity
* @ORM\Table(name="view_version")
*/
class ViewVersion extends Entity\Base {


    /**
    * @ORM\ManyToOne(targetEntity="\Gutensite\CmsBundle\Entity\View\View", inversedBy="versions")
    * @ORM\JoinColumn(name="viewId", referencedColumnName="id")
    */
    protected $view;

    /**
    * The primary view entity that this version belongs to.
    * @ORM\Column(type="integer", nullable=true)
    */
    protected $viewId;

}

但是,我发现只要设置了$ view-&gt; publishedId,由于外键约束(即使它是单向的),视图也无法从数据库中删除。所以我必须在删除之前打破该外键链接。我认为没关系。我在此处发布了有关详细信息:Overlapping Entity Association causing Database Foreign Key Constraint Errors when Removing Entity