复合键和表单

时间:2013-04-09 18:30:55

标签: symfony doctrine-orm symfony-2.2

我的数据库(简化版)中有以下关联:

db schema

这是一个多对多关联,但在加入表上有一个属性,所以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()个对象,但没有运气。

3 个答案:

答案 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à!