Reputation: 7135
I have a Person
entity and an Address
entity, set up with a bi-directional one-to-one relationship, with the FK on 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;
}
The Address
Entity does NOT have a dedicated primary key, it instead derives its identity through the foreign key relationship with Person
, as explained here
The form I have for creating a new Person also embeds the form for Address. When the form is submitted, this controller action is executed:
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(),
);
}
I've inspected the data, and $person
has its $address
property filled out with the proper form details as expected. However, once the $person
object is persisted, I get the error:
A new entity was found through the relationship 'Acme\AcmeBundle\Entity\Person#address' that was not configured to cascade persist operations for entity...
I've tried a couple of things and none seem to work:
cascade={"persist"}
on the OneToOne relationship definition on the Person object. Doing so results in error:
Entity of type Acme\AcmeBundle\Entity\Address is missing an assigned ID for field 'person'...
Person#setAddress
method, I've taken the $address
parameter and manually called $address->setPerson($this)
on it. Doesn't work either.Address
object before saving the Person
object, and it can't because it needs to know the ID of the associated Person
first.
For instance, If I alter the the persist code to something like this, it works:
...
// 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();
...
How can I do this properly? I want to retain the ability to embed forms with this type of one-to-one relationship, but things are starting to get complicated. How do I get Doctrine to flush the $person
object before flushing the $address
object, without manually doing it myself like above?
Upvotes: 3
Views: 2123
Reputation: 86
Your mapping is incorrect. You are using @ORM\Id
incorrectly on $person.
If you haven't yet, add a real $id to Address and add again `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;
}
If Person were the owning side, Address should also be automatically persisted by Doctrine, don't know your model but maybe you should consider changing it.
Upvotes: 1
Reputation: 48865
Keep the cascade=persist.
Then modify Person::setAddress
class Person
{
public function setAddress($address)
{
$this->address = $address;
$address->setPerson($this); //*** This is what you are missing ***
This is a very common question but it's hard to search for.
Upvotes: 4