Cloning an entity in Symfony2 saves changes to original record and cloned record when persisted via Doctrine
doctrine-orm, php, symfony
Solution
`Persist` is only needed when you attaching something to your `Entity Manager` . But in your case original "`$view` record id (e.g. 33)" already within it. So basically, what happens:
$view1 = new View();
$view1->text = '1';
$em->persist($view1);
$em->flush();
Now your have one record with text == '1' stored. Then:
$view1->text = 'one'; //important!
$view2 = new View();
$view2->text = 'two';
$view3 = new View();
$view3->text = 'three';
$em->persist($view2);
$em->flush();
Call of `flush()` updates your `$view1`, inserts your `$view2`, and ignores your `$view3` since last is not persisted. As a result you have two records 'one' and 'two'.
It is possible to call `flush()` for selected objects. So call of `$em->flush($view2)` will only insert `$view2` and leave `$view1` untouched.
In your simple example it will work.
But make sure `$em->flush()` won't happen any further.
Otherwise to be sure that your `$view1` will stay unchanged try to `$em->refresh($view1)` it.
Problem
I have a form that allows me to save a record or duplicate it. The form saves the record as a `$view` entity, which happens to have multiple associated entities, e.g. `$viewVersion` that are managed by the form builder in a formType with nested entities (this probably is irrelevant). If I make changes and submit the form to "duplicate", the code clones the `$view` object with a function on my entity that unsets the `$view->id` and other associations. This forces Doctrine to make a new record when it persists the record to the database. This works perfectly. Hurray! BUT, the changes made to the record are ALSO persisted to the original entity that was cloned (and consequently saved to the database). So it is saving these changes to TWO database records. I happen to like this functionality, but I need to understand WHY it's doing it so it doesn't break later. Here are the relevant bits of code in summary: ``` // File: CmsBundle/Controller/AdminEditController.php // Get the Entity Manager $em = $this->getDoctrine()->getManager(); // Get the View based on the requested ID // Is there some magic that happens here to make the entity manager track this $view entity? $view = $em->getRepository("GutensiteCmsBundle:View\View")->find($request->query->get('id')); // Various bits of code to do whatever I want before a save // ... if ($request->isMethod( 'POST' )) { $form->handleRequest($request); if( $form->isValid() ) { // Duplicate the view entity if the view button is pushed if( $form->has('duplicate') && $form->get('duplicate')->isClicked() ) { $view = clone $view; } // Persist the cloned view $em->persist($view); $em->flush(); } } ``` The View entity has a special clone function that gets triggered on a clone, which resets the ids of the cloned versions: ``` // File: CmsBundle/Entity/View.php public function __clone() { if($this->id) { $this->setId(null); $this->setLockVersion(1); $this->setPublished(null); // Clone associated entities and reassociate with THIS version (even though there is no id yet, there will be when it persists) // clone the current version (which also has a clone function like this) $version = clone $this->getVersion(); // reset the viewid with a custom function $version->resetView(); // Add this cloned verion to the version history $this->addVersion($version); } ``` I've read a lot about cloning, and consistently I'm told that you don't need to detach the original `$view` from the entity manager. Besides, I've tried, and it didn't do any good. The changes to $view, which were submitted by the form and processed to $view prior to the cloning, are still saved to the original `$view` record id (e.g. 33), as well as to the new cloned record (e.g. 62). So two persists are happening, even though, only one persist is called on a single entity. What is going on? Update I am told that if you load an entity with the entity manager, it is being tracked by the entity manager. So if you call `flush()` at any time, any changes will be persisted, even if you did not call `persist($view)` on the entity. So when I clone the entity, the entity manager is effectively managing 2 entities: the original and the clone. I've tried detaching the view from the entity manager before the clone in two ways: ``` // detach method 1 $em->detach($view); $em->flush(); // detach method 2 $em->refresh($view); $em->flush(); // clone the view after detaching the first entity. $view = clone $view; ``` But the entity manager still persists the changes to the original `$view` record. I also tried the suggestion to add `unset($this->_entityPersister, $this->_identifier);` to my custom `__clone()` method. But that also did not detach the the original entity or the cloned version from the entity manager. The changes were saved to both the old record and the new record. Nothing seems to make the entity manager ignore the original entity. Useful References - How Clone Works - Implement Your Own Clone - Clone Nested