Symfony关联将OneToOne和OneToMany映射到同一个实体


Symfony Association Mapping OneToOne and OneToMany to Same Entity

我有一个表示主页记录的View实体,然后我有一个称为ViewVersion的关联实体,该实体存储该实体随时间变化的多个版本。View实体在VersionId字段中设置当前"已发布"的ViewVersion。这就形成了一个简单的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()所做的那样。

注意:其他的答案都不完整。

引用:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.htmlhttp://doctrine-orm.readthedocs.org/en/2.0.x/reference/association-mapping.html one-to-many-bidirectional

通常,我发现最好的方法是用另一种方式解决这个问题。

我以前见过的一个常见模式是使用一个表来保存所有记录,并有一个'active'标志。

如果您的查询选择活动的工作方式如下:

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)很好地处理,该系统允许您定义基本视图类,然后对于"ViewRevision"模型,扩展View并添加"modified on"类型时间戳。

根据@jmather的建议,我决定这个模型是"可以的",因为我需要一个其他实体可以访问的单一视图实体(例如,路由指向单个视图的url,即:"页面")。

我已经将视图的OneToOne关系更改为单向的,因为ViewVersion已经通过另一个OneToMany关联回视图(所以它不需要两个路径返回)。

这允许我保持一个简单的方法$view->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-> publishhedid,由于外键约束(即使它是单向的),视图就不能从数据库中删除。所以我必须在移除前打破外键链接。我认为这很好。我在这里发布了详细信息:删除实体

时,重叠实体关联导致数据库外键约束错误