【问题标题】:How to persist entities from an embedded form submission?如何从嵌入式表单提交中持久化实体?
【发布时间】:2014-04-22 13:00:22
【问题描述】:

我有一个Person 实体和一个Address 实体,设置为双向一对一关系,FK 位于Address

...

class Person
{
    ...
    /**
     * @ORM\OneToOne(targetEntity="Address", mappedBy="person")
     */
    protected $address;
    ...
}

...

class Address
{
    ...
    /**
     * @ORM\Id
     * @ORM\OneToOne(targetEntity="Person", inversedBy="address")
     * @ORM\JoinColumn(name="personID", referencedColumnName="id")
     */
    protected $person;
}

Address 实体没有专用的主键,而是通过与Person 的外键关系派生其身份,如explained here

我用于创建新人员的表单也嵌入了地址表单。提交表单时,会执行此控制器操作:

public function createAction(Request $request)
{
    $person = new Person();

    $form = $this->createCreateForm($person);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($person);
        $em->flush();

        return $this->redirect($this->generateUrl('people'));
    }

    return array(
        'person' => $person,
        'form' => $form->createView(),
    );
}

我检查了数据,$person$address 属性按预期填写了正确的表单详细信息。但是,一旦 $person 对象被持久化,我就会收到错误消息:

通过关系发现了一个新实体 'Acme\AcmeBundle\Entity\Person#address' 未配置为 实体的级联持久化操作...

我已经尝试了几件事,但似乎都没有工作:

  1. 在 Person 对象的 OneToOne 关系定义上设置 cascade={"persist"}。这样做会导致错误:

    Acme\AcmeBundle\Entity\Address 类型的实体缺少分配的 字段“人”的 ID...

  2. Person#setAddress 方法上,我采用了$address 参数并在其上手动调用$address->setPerson($this)。也不行。
我的问题似乎是 Doctrine 在保存 Person 对象之前尝试保存 Address 对象,但它不能,因为它首先需要知道关联 Person 的 ID。

例如,如果我将持久代码更改为这样的内容,它可以工作:

...
// Pull out the address data and remove it from the Person object
$address = $person->getAddress();
$person->setAddress(null);

// Save the person object and flush so we get an ID
$em->persist($person);
$em->flush();

// Now set the person object on the address and save the address
$address->setPerson($person);
$em->persist($address);
$em->flush();
...

我怎样才能正确地做到这一点?我想保留使用这种一对一关系嵌入表单的能力,但事情开始变得复杂。如何让 Doctrine 在刷新 $address 对象之前刷新 $person 对象,而不像上面那样自己手动执行?

【问题讨论】:

    标签: symfony doctrine-orm


    【解决方案1】:

    保持级联=持续。

    然后修改Person::setAddress

    class Person
    {
        public function setAddress($address)
        {
            $this->address = $address;
            $address->setPerson($this); //*** This is what you are missing ***
    

    这是一个非常常见的问题,但很难搜索。

    【讨论】:

    • 这会导致错误:Entity of type Acme\AcmeBundle\Entity\Address has identity through a foreign entity Acme\AcmeBundle\Entity\Person, 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 'Acme\AcmeBundle\Entity\DetailsCS'. 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。基于这个有用的错误,我可能确实需要使用我的“解决方法”
    • 我会给地址实体它自己的简单自动增量 ID,并使人员成为常规关系而不是主 ID。是的,它是一个额外的数据库列,但我认为您会发现它可以省去很多麻烦。
    【解决方案2】:

    您的映射不正确。您在 $person 上错误地使用了 @ORM\Id

    如果您还没有,添加一个真正的 $id 到 Address 并再次添加 `cascade={"persist"}。

    class Person
    {
        ...
        /**
         * @ORM\OneToOne(targetEntity="Address", mappedBy="person", cascade={"persist"})
         */
        protected $address;
        ...
    }
    
    ...
    
    class Address
    {
        ...
        /**
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        protected $id;
    
        /**
         * @ORM\OneToOne(targetEntity="Person", inversedBy="address")
         * @ORM\JoinColumn(name="personID", referencedColumnName="id")
         */
        protected $person;
    }
    

    如果 Person 是拥有方,Address 也应该由 Doctrine 自动持久化,不知道你的模型但也许你应该考虑改变它。

    【讨论】:

    • 我实际上不想在Address 上添加专用主键。请参阅this page,它解释了我使用外键作为Address 的主键
    • 我更新了我的问题以包含我原本应该这样做的细节。
    猜你喜欢
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 2011-10-14
    • 2015-07-01
    相关资源
    最近更新 更多