我的数据库(简化版)中有以下关联:
这是一个多对多关联,但在加入表上有一个属性,所以I have to use One-To-Many/Many-To-One associations。
我有一个表单,我可以在一个订单项中添加尽可能多的关系并同时创建它(主要受文档中How to Embed a Collection of Forms教程的启发。
当我发布表单时,我收到以下错误:
类型为TEST \ MyBundle \ Entity \ Relation的实体具有标识 一个外国实体TEST \ MyBundle \ Entity \ Order,但是这个实体 没有自己的身份。你必须调用EntityManager#persist() 相关实体并确保生成标识符 在尝试坚持'TEST \ MyBundle \ Entity \ Relation'之前。如果 后插入ID生成(例如MySQL自动增量或 PostgreSQL SERIAL)这意味着你必须调用EntityManager #flush() 在两个持续的操作之间。
我理解这个错误,因为Doctrine会尝试保留与订单相关的Relation
个对象,因为我在cascade={"persist"}
关系上有OneToMany
选项。但是我怎么能避免这种行为?
我尝试删除cascade={"persist"}
并手动保留实体,但我得到了相同的错误(因为我需要flush()
才能获取ID,当我这样做时,我也有同样的错误错误信息)
我还尝试在detach()
之前Relation
flush()
个对象,但没有运气。
答案 0 :(得分:3)
如果您正在使用具有复合键的连接表,2)表单组件,以及3)连接表是由表单组件构建的实体,则此问题似乎是唯一的。集合'领域。我看到很多人遇到问题但没有很多解决方案,所以我想我会分享我的。
我想保留我的复合主键,因为我想确保两个外键中只有一个实例会在数据库中保留。运用 以this entity setup为例
/** @Entity */
class Order
{
/** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
private $items;
public function __construct(Customer $customer)
{
$this->items = new Doctrine\Common\Collections\ArrayCollection();
}
}
/** @Entity */
class Product
{
/** @OneToMany(targetEntity="OrderItem", mappedBy="product") */
private $orders;
.....
public function __construct(Customer $customer)
{
$this->orders = new Doctrine\Common\Collections\ArrayCollection();
}
}
/** @Entity */
class OrderItem
{
/** @Id @ManyToOne(targetEntity="Order") */
private $order;
/** @Id @ManyToOne(targetEntity="Product") */
private $product;
/** @Column(type="integer") */
private $amount = 1;
}
我遇到的问题,如果我在表单中构建Order
对象,其集合字段为OrderItem
s,我将无法保存OrderItem实体首先保存了Order Entity(因为doctrine / SQL需要复合键的order id),但是Doctrine EntityManager并不允许我保存具有OrderItem属性的Order对象(因为它坚持将它们保存为大量一起)。您无法关闭级联,因为它会抱怨您尚未首先保存关联的实体,并且在保存Order
之前无法保存关联的实体。真是个难题。 我的解决方案是删除关联的实体,保存Order
,然后将关联的实体重新引入Order对象并再次保存。首先,我创建了一个ArrayCollection属性$items
class Order
{
.....
public function setItemsArray(Doctrine\Common\Collections\ArrayCollection $itemsArray = null){
if(null){
$this->items->clear();
}else{
$this->items = $itemsArray;
}
....
}
然后在我的Controller中处理Order的表单。
//get entity manager
$em = $this->getDoctrine()->getManager();
//get order information (with items)
$order = $form->getData();
//pull out items array from order
$items = $order->getItems();
//clear the items from the order
$order->setItemsArray(null);
//persist and flush the Order object
$em->persist($order);
$em->flush();
//reintroduce the order items to the order object
$order->setItemsArray($items);
//persist and flush the Order object again ):
$em->persist($order);
$em->flush();
很糟糕,你必须坚持并冲洗两次(详见Persist object with two foreign identities in doctrine)。但这是对你的教条,凭借它的所有力量,它肯定可以让你陷入困境。但幸运的是,您只需在创建新对象而不是编辑时执行此操作,因为该对象已在数据库中。
答案 1 :(得分:1)
您需要先保留并刷新原始内容,然后才能保留并刷新关系记录。您在错误原因中100%正确。
我从图中假设您正在尝试添加和订购以及与联系人的关系?如果是这样,您需要持久并刷新订单,然后才能坚持并刷新关系。或者您可以将主键添加到Relation表中。
答案 2 :(得分:1)
我最终在Relation
表上创建了一个单独的主键(而不是复合键)
看起来它是一个肮脏的修复,我相信有更好的方法来处理这种情况,但它现在有效。
这是我的Relations
实体:
/**
* Relation
*
* @ORM\Entity
*/
class Relation
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="Contact", inversedBy="relation")
*/
protected $contact;
/**
* @ORM\ManyToOne(targetEntity="Order", inversedBy="relation")
*/
protected $order;
/**
* @var integer
*
* @ORM\Column(name="invoice", type="integer", nullable=true)
*/
private $invoice;
//Rest of the entity...
然后,我在cascade={"persist"}
与[{1}}的{{1}}关系中添加了OneToMany
选项:
Order
Etvoilà!