【问题标题】:OneToMany relationship not persisting along with new entitiesOneToMany 关系不会与新实体一起存在
【发布时间】:2014-01-13 16:30:06
【问题描述】:

当我尝试使用 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;

产品项目

/**
 * @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,但我该怎么做呢?

我只在尝试持久化新实体时出现此错误,如果实体之前已持久化,我可以成功添加任意数量的项目

【问题讨论】:

  • 可能你忘记在ProductItem Product上添加注解@ORM\Entity

标签: symfony doctrine-orm


【解决方案1】:

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

问题是您的 Product 实体具有级联持久化(这通常很好),它首先尝试持久化 ProductItemProductItem 实体无法持久化,因为它们需要先持久化 Product 及其 ID(复合键 (产品,物品)。

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

第一个我没有使用它,但您可以简单地删除一个复合键并使用带有外键的标准 id 到 Product

第二 - 更好这可能看起来像 hack,但相信我,这是你现在能做的最好的事情。它不需要对数据库结构进行任何更改,并且可以毫无问题地使用表单集合。

我的代码中的代码片段,文章部分具有(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();

【讨论】:

  • 完美运行,它不是最干净的方式,但却是最有效的方式。谢谢!!
【解决方案2】:

您没有完全遵循文档。您可以执行以下操作来测试单个 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,但就像我说的那样,动态的东西要多一些工作。

【讨论】:

  • 我已经实现了 javascript,并正在努力在编辑操作中添加/删除 productItems(一旦创建了产品)。我的问题是,当我尝试持久化 ProductItem 集合时会引发异常,因为新产品尚未持久化。我不知道的是如何先保留 Product 然后是 ProductItem 集合以避免该异常
【解决方案3】:

注释错误...级联持久化在关系的错误一侧

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


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

实现此目的的另一种方法(例如无法注释)是设置表单by_reference

【讨论】:

  • 现在我得到这个错误`A new entity was found through the relationship Product#items that was not configured to cascade persist operations for entity: Test. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}).
【解决方案4】:

IMO,您的问题与您的控制器无关,而是与您的实体有关。您似乎想在您的 Product 和 Item 之间创建一个 ManyToMany,而不是创建一个 ProductItem 类,该类应该充当表示您的关系的中间对象。此外,这个中间对象没有 id 生成策略。这就是 Doctrine 向您解释的原因,您必须首先持久化/刷新所有新项目,然后持久化/刷新您的产品,以便能够获取中间对象的 id。

【讨论】:

  • 肯定它应该是两个实体之间的多对多,但我需要在它们的关系中添加一些值,这就是我使用 OneToMany 的原因。我一直在寻找一些关于如何处理这种情况的教程,但我没有找到任何东西。也许我需要尝试不同的方法...谢谢!
【解决方案5】:

在处理附加了 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-10
    • 1970-01-01
    • 2020-11-09
    相关资源
    最近更新 更多