我有一个表示主页记录的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
实体,但映射的关联需要两个单独的变量,例如View
和ViewAll
。我不太确定内部是如何为关联工作的,以及如何使用带有映射的引用变量。
或者,我可以摆脱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,由于外键约束(即使它是单向的),视图就不能从数据库中删除。所以我必须在移除前打破外键链接。我认为这很好。我在这里发布了详细信息:删除实体