2

I have a CustomerAccount entity. After that entity has had changes made to it via a form, but before the entity has been persisted to the database, I need to fetch a new copy of the same CustomerAccount with the entity as it currently exists in the database. The reason I need to do this is I want to fire off a changed event with both the old and new data in my service.

One hack I used was $oldAccount = unserialize(serialize($account)); and passing the old into my service, but thats really hackish.

What I would really like to do is have Doctrine pull back a copy of the original entity (while keeping the changes to the new version).

Is this even possible?

Update

It appears what I really want to do is ultimately impossible at this time with the way Doctrine is architected.

Update 2

I added the solution I ultimately ended up using at the bottom. I'm not completely happy with it because it feels hackish, but it gets the job done and allows me to move on.

Dan Morphis
  • 1,708
  • 19
  • 23
  • Have a look at [*object cloning*](http://stackoverflow.com/a/2144654/853360) i.e [*Clone*](http://php.net/manual/en/internals2.opcodes.clone.php) – M Khalid Junaid Sep 10 '14 at 19:44
  • Have a look at that: http://stackoverflow.com/questions/9057558/is-there-a-built-in-way-to-get-all-of-the-changed-updated-fields-in-a-doctrine-2 – fsperrle Sep 10 '14 at 19:47
  • Yes, I'm already doing that in another place. But I don't want that to be my hammer. This is one of the times when I want to tell Doctrine "No, please go re-fetch the entity from the db. I really, really know what I'm doing." – Dan Morphis Sep 10 '14 at 19:51
  • something like http://doctrine1.readthedocs.org/en/latest/en/manual/component-overview.html#refreshing-records ? – fsperrle Sep 10 '14 at 19:58
  • @Fabian, no. That would wipe out the changes on the original entity with the data in the database. What I really want, are two entities. One pre-changes, one post-changes. But without having to go through gyrations, or leak abstractions. – Dan Morphis Sep 10 '14 at 20:02
  • Have you considered using Doctrine 2's built in change tracking policies? http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html. Doctrine is not going to manage two different versions of the same object for you. – Cerad Sep 10 '14 at 20:05
  • You can also listen to the preUpdate event and use the getEntityChangeSet functionality. http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#preupdate – Cerad Sep 10 '14 at 20:09
  • @Cerad, I don't want Doctrine to manage the pre-change entity. I just want it to pull back a new entity (but only in this specific case). Admittedly, what I'm doing is really an edge case for Doctrine. – Dan Morphis Sep 10 '14 at 20:10
  • Refresh will pull the original data back in but it will override any changes you made since the original fetch. So yes, you are asking D2 to manage two versions of the same entity. And that is not even an edge case. I suppose you use could two different entity managers. But cloning will be your best bet. – Cerad Sep 10 '14 at 20:24

2 Answers2

3

It depends.

I mean, Doctrine2 use the IdentityMap that prevents you "accidentally" query the db for the same object over and over again into the same request. The only way to force doctrine fetch entity object again is to detach the entity from the entity manager and request entity again.

This, however, could lead to some strange behaviour that could "slip" out of your control:

  • you can't persist again a detached object
  • if you try to persist an object that is related ("linked") to your detached entity you will run into troubles (and sometimes is very difficult to debug)

So, why don't you try with php built-in clone function? Maybe is more suitable for you and could save you from a lot of debugging

Code example:

$em = $this->getDoctrine()->getManager();
$fetched_entity = $em->findOnById(12);
$cloned_entity = clone $fetched_entity;
//and so on ...
DonCallisto
  • 29,419
  • 9
  • 72
  • 100
  • I was also suggesting for the clone +1 – M Khalid Junaid Sep 10 '14 at 19:43
  • I went down both of these routes. Cloning doesn't do what I want to do, in the layer I want to do it in. I also tried to detach original, clone, attach clone, refresh clone, detach clone, attach original. But that didn't work either. I have an entity hanging off the CustomerAccount which holds a device. That device entity was never cloned. I could make the CustomerAccount fully implement clone, but thats seems like a hack. – Dan Morphis Sep 10 '14 at 19:46
  • @DanMorphis: I don't understand what you mean. Could you be more accurate? – DonCallisto Sep 10 '14 at 19:49
  • @DonCallisto, Which part do you need more info on? – Dan Morphis Sep 10 '14 at 19:52
  • @DanMorphis: why can't you use clone or defetch? We need more details in order to help you in a better way (I noticed only now your update into comments, I'll check) – DonCallisto Sep 10 '14 at 20:08
  • @DanMorphis: however, you can't tell doctrine2 to fetch again an object already fetched if is manage into IdentityMap. – DonCallisto Sep 10 '14 at 20:09
  • @DonCallisto, Cloning will work, /if/ I clone the entity pre-change, all the way up in my controller, and then pass the changed entity *and* the clone into my service. To me, this smells of a leaky abstraction because I don't care about the pre-change entity in my controller, only in my service. I saw this answer http://stackoverflow.com/a/14771321/70235 of yours. But can I then persist $a? – Dan Morphis Sep 10 '14 at 20:18
  • @DanMorphis: call `clear()` or `detach()` is the same, I'm afraid that you could not persist if entity manager doesn't handle it anymore. Maybe after you should call `merge()` method of entity manager. I'm not sure but could work – DonCallisto Sep 10 '14 at 20:22
3

Here is the ultimate solution I ended up using. I created a duplicate entity manager in my config.yml and retrieved a second copy of the entity from the duplicate entity manager. Because I won't make any changes to the entity retrieved by the duplicate entity manager, this solution was the best for my use case.

Dan Morphis
  • 1,708
  • 19
  • 23