Symfony 如何从嵌入式表单提交中持久化实体?

Symfony 如何从嵌入式表单提交中持久化实体?,symfony,doctrine-orm,Symfony,Doctrine Orm,我有一个人实体和一个地址实体,通过双向一对一关系设置,FK位于地址: ... class Person { ... /** * @ORM\OneToOne(targetEntity="Address", mappedBy="person") */ protected $address; ... } ... class Address { ... /** * @ORM\Id * @ORM\OneToOne

我有一个
实体和一个
地址
实体,通过双向一对一关系设置,FK位于
地址

...

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;
}
地址
实体没有专用的主键,而是通过与
人员
的外键关系派生其身份,如下所示

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

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”未配置为 实体的级联持久化操作

我试过几种方法,但似乎都不管用:

  • 在Person对象上的OneTONE关系定义上设置
    cascade={“persist”}
    。这样做会导致错误:

    Acme\AcmeBundle\Entity\Address类型的实体缺少分配的 字段“person”的ID
  • Person#setAddress
    方法中,我使用了
    $address
    参数,并在其上手动调用了
    $address->setPerson($this)
    。也不行
  • 我的问题似乎是,条令试图在保存
    对象之前保存
    地址
    对象,但它不能,因为它需要先知道关联的
    的ID

    例如,如果我将persist代码更改为如下内容,它将起作用:

    ...
    // 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();
    ...
    

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

    保持cascade=persist

    然后修改Person::setAddress

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

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

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

    如果您还没有,请向Address添加一个真正的$id,然后再次添加`cascade={“persist”}


    如果人是拥有方,地址也应该被理论自动保留,不知道你的模型,但也许你应该考虑改变它。

    我实际上不想在<代码>地址< />代码中添加一个专用的主键。请参见以下说明:我使用外键作为
    地址的主键
    我更新了我的问题,以包含我原本应该做的详细信息。这导致错误:
    类型为Acme\AcmeBundle\Entity\Address的实体通过外部实体Acme\AcmeBundle\Entity\Person具有标识,然而,这个实体本身没有身份。您必须在相关实体上调用EntityManager#persist(),并确保在尝试持久化“Acme\AcmeBundle\entity\DetailsCS”之前生成了标识符。在生成插入后ID(如MySQL自动增量或PostgreSQL串行)的情况下,这意味着您必须在两个持久化操作之间调用EntityManager#flush()。基于这个有用的错误,我可能确实需要使用我的“变通方法”,我会给地址实体它自己的简单自动递增id,并使person成为常规关系而不是主id。是的,它是一个额外的数据库列,但我想你会发现它节省了很多麻烦。
    
    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;
    }