问题
I have object A which contains a collection of objects B which in turn contain a collection of objects C which again contains a collection of object D.
I need to retrieve object A from DB, create a form from it, change some values in fields of the form (in the future also add and remove elements from the collections) and persist on DB a new object A with all the appropriate references to the new B, new C and new D.
The object CVC below is my A.
If I do:
$storedCVC = $this->getDoctrine()
->getRepository('AppBundle:CVC')
->find($id);
$clonedCVC = clone $storedCVC;
$form = $this->createForm(new CVCFormType(), $ClonedCVC);
When I dump the two objects A I see a different references for objects A, but when I dig to objects C, B and D level, the references are the same as the original (what we might expect from a "shallow" clone, see screenshot below).
Nonetheless it properly creates the new object A and (surpsingly) it also persists the new objects B, C and D as well (could it be because I specified the following persist on each father-child relationship):
/**
* @ORM\ManyToOne(targetEntity="CVC", cascade={"persist"}, inversedBy="BenefitItems")
*/
protected $CVC;
Note that BenefitItems is my object B. Of course in object A I have:
/**
* @ORM\oneToMany(targetEntity="BenefitItem", mappedBy="CVC")
*/
protected $BenefitItems;
So far so good (albeit misterious to me). The problem arises when I try to map an entity type field (the typical "category" field), which belongs to object C. When I update that entity on the form, it updates also the original category.
I tried the following to perform a deep clone:
function cloneCVC ($oldCVC) {
$newCVC = new CVC();
foreach ($oldCVC->getBenefitItems() as $oldBI)
{
$newBI = new BenefitItem();
foreach ($oldBI->getBenefitGroups() as $oldBG)
{
$newBG = new BenefitGroup();
foreach ($oldBG->getBenefitSubItems() as $oldSI)
{
$newSI = new BenefitSubItem();
$newSI->setComment($oldSI->getComment());
$newBG->addBenefitSubItem($newSI);
}
$newBG->setBenefitGroupCategory($oldBG->getBenefitGroupCategory());
$newBI->addBenefitGroup($newBG);
}
$newBI->setComment($oldBI->getComment());
$newCVC->addBenefitItem($newBI);
}
return $newCVC;
}
The problem is that the object I create is different, so when I try to call the create form on it it fails:

Any help?
EDIT: the relationship is A->B->C->D so I assume there was a misunderstanding with Stepan. Then we have C->E that is the relationship for the entity type field.
In my code:
A = CVC
B = BenefitItem
C = BenefitGroup
D = BenefitSubItem
E = BenefitGroupCategory
The code suggested below properly works in CVC (I indeed have different references now to the arrays of BenefitItems, see red lines below) but even if I do the following inside CVC:
public function __clone()
{
if ($this->id) {
$this->id = null;
$this->BenefitItems = clone $this->BenefitItems;
}
}
the clone of the benefit items does not happen (see yellow and light blue lines). The code inside the benefit item entity is:
public function __clone()
{
if ($this->id) {
$this->id = null;
$this->BenefitGroups = clone $this->BenefitGroups;
}
}
But it never gets into the if. I changed
$this->id = null
with
$this->idXXX = null
to cause an error but it doesn't get called).
Shall I simply eliminate the if?
Here is the situation visually:

回答1:
You have to implement a __clone()
method in your entities that sets the id to null and clones the relations if desired. Because if you keep the id in the related object it assumes that your new entity A
has a relation to the existing entities B
and C
.
Clone-method for A
:
public function __clone() {
if ($this->id) {
$this->setId(null);
$this->B = clone $this->B;
$this->C = clone $this->C;
}
}
Clone-method for B
and C
:
public function __clone() {
if ($this->id) {
$this->setId(null);
}
}
https://groups.google.com/forum/?fromgroups=#!topic/doctrine-user/Nu2rayrDkgQ
https://doctrine-orm.readthedocs.org/en/latest/cookbook/implementing-wakeup-or-clone.html
来源:https://stackoverflow.com/questions/29347310/symfony2-cloning-entities-with-collections-and-entity-field-type