OneToMany关系不会与新实体一起持续存在

时间:2014-01-13 16:30:07

标签: symfony doctrine-orm

当我尝试使用symfony表单来保留实体集合时,我遇到了一些问题。我跟着official documentation,但由于这个错误,我无法使它工作:

Entity of type ProductItem has identity through a
foreign entity Product, however this entity has no identity itself. You have to call    
EntityManager#persist() on the related  entity and make sure that an identifier was 
generated before trying to persist ProductItem. In case of Post Insert ID 
Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you 
have to call EntityManager#flush() between both persist operations.

我必须与OneToMany关系链接的实体:

产品

/**
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="IDENTITY")
 */
protected $id;

/**
 * @ORM\OneToMany(targetEntity="ProductItem", mappedBy="product",cascade={"persist"})
 */
protected $items;

和ProductItem

/**
 * @ORM\Id()
 * @ORM\ManyToOne(targetEntity="Product", inversedBy="items")
 */
protected $product;

/**
 * @ORM\Id()
 * @ORM\ManyToOne(targetEntity="Item")
 */
protected $item;

这是它添加到表单的方式:

->add('items','collection',array(
            'label' => false,
            'type' => new ProductItemType(),
            'allow_add' => true,
            'allow_delete' => true,
            'by_reference' => false))

这是控制器动作:

public function newAction()
{
    $product= new Product();

    $form = $this->createForm(new ProductType(), $product);
    if($request->isMethod("POST"))
    {
        $form->handleRequest($request);
        if($form->isValid())
        {
            $em = $this->getDoctrine()->getManager();
            $em->persist($product);
            $em->flush();
        }
    }
}

我在控制器中肯定做错了,因为正如错误信息所说,我必须在添加$ productItems之前保留$ product,但我该怎么办呢?

我在尝试保留新实体时只会出现此错误,如果该实体之前已被保留,我可以添加为可能的项目,我想成功

5 个答案:

答案 0 :(得分:6)

上周我遇到了完全相同的问题,这是我在一些阅读和测试后找到的解决方案。

问题是您的产品实体具有级联持久性(通常是好的)并且它首先尝试持久化ProductItemProductItem实体无法持久化,因为它们要求首先保留产品及其ID (复合键(产品,项目)。

有两种方法可以解决这个问题:

第一我没有使用它,但您只需删除一个复合键,并使用标准ID和外键Product

第二名 - 更好这可能看起来像黑客,但相信我这是你现在能做的最好的事情。它不需要对数据库结构进行任何更改,也可以使用表单集合而没有任何问题。

我的代码中的代码片段,文章部分具有(article_id,random_hash)的复合键。临时设置一对多引用一个空数组,保留它,添加原始数据并再次持久(和刷新)。

    if ($form->isValid())
    {
        $manager = $this->getDoctrine()->getManager();

        $articleSections = $article->getArticleSections();
        $article->setArticleSections(array());  // this won't trigger cascade persist
        $manager->persist($article);
        $manager->flush();

        $article->setArticleSections($articleSections);
        $manager->persist($article);
        $manager->flush();

答案 1 :(得分:1)

您没有完全遵循文档。您可以执行以下操作来测试单个item,但如果您想动态添加和删除项目(看起来像您这样做),您还需要实现文档中包含的所有javascript你链接到。

$product= new Product();
$productItem = new ProductItem();

// $items must be an arraycollection
$product->getItems()->add($productItem);

$form = $this->createForm(new ProductType(), $product);
if($request->isMethod("POST"))
{
    $form->handleRequest($request);
    if($form->isValid())
    {
        $em = $this->getDoctrine()->getManager();
        $em->persist($productItem);
        $em->persist($product);
        $em->flush();
    }
}

所以这应该适用于单个静态item,但就像我说的那样,动态的东西是更多的工作。

答案 2 :(得分:0)

注释是错误的......级联持续存在于关系的错误一侧

/**
 * @ORM\OneToMany(targetEntity="ProductItem", mappedBy="product")
 */
protected $items;


/**
 * @ORM\Id()
 * @ORM\ManyToOne(targetEntity="Product", inversedBy="items", cascade={"persist"})
 */
protected $product;

实现此目的的另一种方法(例如,注释不可能)是设置形式by_reference

答案 3 :(得分:0)

IMO,您的问题与您的控制器无关,而与您的实体无关。您似乎希望在Product和Item之间创建一个ManyToMany,而不是创建一个ProductItem类,它应该表现为表示关系的中间对象。另外,该中间对象没有id生成策略。这就是Doctrine为您解释的原因,您必须先保留/清除所有新项目,然后保留/冲洗您的产品,以便能够获取中间对象的ID。

答案 4 :(得分:0)

在使用附加CollectionType字段的表单的工作期间也遇到此问题。另一种可以解决这个问题并且在学说官方文档中也提到的方法如下:

public function newAction()
{
    $product= new Product();

    $form = $this->createForm(new ProductType(), $product);
    if($request->isMethod("POST"))
    {
        $form->handleRequest($request);
        if($form->isValid())
        {
            foreach ($product->getItems() as $item)
            {
                $item->setProduct($product);
            }

            $em = $this->getDoctrine()->getManager();
            $em->persist($product);
            $em->flush();
        }
    }
}

简单来说,您应该手动提供链接项目的产品链接 - 以下文章的“建立关联”部分对此进行了描述:http://docs.doctrine-project.org/en/latest/reference/working-with-associations.html#working-with-associations