Flush entities one by one after ObjectManager:merge() - php

I have a Doctrine\ORM entity, consisting of new and existing database entities, created from Doctrine ObjectManager:merge(). Before flushing, I want to prevent duplicates (the context is an api where users could send e.g. duplicate city names within the same request).
As mentioned in this github issue it is recommended to flush after every persist() operation to be able to query my database for existing records including the ones persisted in this request. How could I do that?
My code is:
...
$object = $mapper->map($objectRaw, new $modelClassName());
$object = $em->merge($object);
# I need to intervene here I suppose
$em->flush();
I tried:
the hint mentioned on the github issue that dql would trigger a flush automatically, but after merge() that would imho mean that all new entites would be flushed, which I do not want before having checked for duplicates
lots of Lifecycle Events combinations, but I think that is not the way, the relevant ones already happened in the merge
It seems that I need a way to flush each entity one by one and do my checks at that time, something like:
Store scheduled insertions from UnitOfWork.
Clear list of scheduled insertions.
Persist and flush every insertion one by one and do my checks.
Or another idea - thank you very much!
Edit: Another idea would probably be to simply do my checks and manipulations on the object before $em->merge($object);. But you would loose the possibility of matching against repositories. So still, the question keeps me busy...

Related

Flush only certain entities in single transaction

in my application I do write to a read model table (think CQRS) at certain times. While doing so, I also have to remove older read models. So at any given point I need to:
Remove entities a,b,c
Persist entities x,y,z
In order to maintain a valid read model throughout the lifecycle, I would like to encapsulate this process in a single transaction. Doctrine does provide the necessary means.
However, I also must guarantee that no other entities are being flushed in the process. Sadly, calling doctrine's $em->getConnection()->commit(); seems to to flush the whole unit of work. But according to the docs I have to call that to finalise my transaction.
I cannot introduce a second entity manager to only take of my read model entities as they are in the same namespace as the other entities and apparently that is not the way the doctrine-orm-bundle is supposed to be used.
The only other approach I see is to work on a lower level and circumvent the EntityManager and UnitOfWork completely, but I would like to guarantee transactional integrity and do not see a way to do so without em/ouw.
TLDR: The way your application works might warrant that update-concerns are completely separable into two independent sets, but that's unusual, and fragile (usually not even true at the time of making the assertion). The proper way to model that is using separate EntityManagers for each of the sets, and by manually guarding their interconnections is the semantics of "how they are independent" coded into the system.
Details:
If I understand the question correctly, you are facing a design flaw here. You have two sets of updates (one for the read models and one for the other entities) and you mix them (because you want both in the same entity manager) while you also want to separate them (by the means of a separate transaction). In general, this won't work.
For example, let's think non-read-model entity instance A is just created in-memory (so it has no ID yet) and based on this you decide to reference it with read-model entity instance R. The R->A relationship is valid in memory, but now you expect to be able to flush only read model entities but not others. I.e. when you try to persist+flush R it will reference a non-existing foreign key and your RDBMS will hopefully fail the transaction. On a high-level, this is because a connected in-memory graph should be consistent data in its entirety and when you try to split its valid changes into subsets, you're rearranging the order of those changes implicitly, which may introduce temporary inconsistency, and then your transaction commit might just be at such a point.
Of course, it may happen that you know some rule why such a thing will never happen and why consistent state of each set is warranted in a fashion that is completely independent from the other set. But then you need to write your code reflecting that separation; the way you can do that is to use two entity managers. In that case your code will clearly cope with the two distinct transactions, their separation and how exactly that is consistent from both sides. But even in this case, to avoid clashes of updates, you probably need to have rules outlining a one-way visibility between the two sets, which will also imply an order to committing transactions. This is because transactions in a connected graph can be nested, but not "only overlap", because at each transaction commit you are asking the ORM to sync the consistent in-memory data of the transaction scope to the RDBMS.
I know you mentioned that you do not want to use two EMs, because read-model entities "are in the same namespace as the other entities and apparently that is not the way the doctrine-orm-bundle is supposed to be used".
The namespace does not really matter. You can use them separately in the two managers. You can even interconnect them, if you a) properly merge() them and b) cater for the above mentioned consistency you need to provide for both EM's transactions (because now they are working on one connected graph).
You should elaborate what exactly you refer to by saying "that is not the way the doctrine-orm-bundle is supposed to be used" -- probably there's an error in the original suggestion or something wrong with the way that suggestion is applied to this problem.

Force doctrine to always refresh

I've got a script that fetches data from a database using doctrine. Sometimes it needs to fetch the data for the same entity, the second time however it uses the identity map and therefor might go out of sync with the database (another process can modify the entities in the db). One solution that we tried was to set the query hint Query::HINT_REFRESH before we run the DQL query. We however would like to use it also with simple findBy(..) calls but that doesn't seem to work? We would also like to be able to set it globally per process so that all the doctrine SELECT queries that are run in that context would actually fetch the entities from the DB. We tried to set the $em->getConfiguration()->setDefaultQueryHint(Query::HINT_REFRESH, true); but again that doesn't seem to work?
Doctrine explicitly warns you that it is not meant to be used without a cache.
However if want to ignore this, then Cerad's comment (also mentioned in in this answer) sound right. If you want to do it on every query though you might look into hooking into a doctrine event, unfortunately there is no event for preLoad, only postLoad, but if you really don't care about performance you could create a postLoad listener which first gets the class and id of the loaded entity, calls clear on the entity manager and finally reloads it. Sounds very wrong to me though, I wash my hands of it :-)

Handling relational tables as entities with Doctrine

I need some help dealing with a relational table that is an entity due to the existence of an additional property.
Here is a gist of the entities in question: https://gist.github.com/chasepeeler/efd7efd890c58eafb81f
Do I have something configured wrong that is forcing me to do the flush in controller.php line 15?
I've also tried just updating the rank attribute of the queueItem record in the Queue::queueItems collection, but when I do that, it doesn't even save the changes to the database.
$queueItems->clear() does the same thing as clearQueueItems, but one time.
And if you want to override current queue state, you should just implement and call setQueueItems(ArrayCollection $queueItemList) method.
UnitOfWork will compute your changes to insert and remove new/deleted items.
Every OneToMany annotated field should implement setItems, addItem and removeItem methods, where Item is related entity name.
Your sortQueue method shouldn't persist and commit changes into database.
It should only return a sorted Collection.
Maybe I didn't get that, it's hard to say what you want to achieve, controller's code says me nothing.

Doctrine refresh copy of entity

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.
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 ...
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.

Doctrine2 should automatically merge associated entities

I am building an order process, in which an Order object is built through many steps. After each step I put the partly finished Order object into the session, and in the final step I save it into the database.
During the steps I load other, (already existing) associated objects into my Order object(eg. DiscountCoupon). The problem is that when I save my Order to the session and then load it in the next step, all associated entities will be detached. So when I want to save it to the database, the EntityManager throws an exception, which asks for setting a cascade=persist on the relationship.
Of course, I dont need to persist those objects(they are already in the database). The obvious solution could be to change those associated objects to merged ones(using EntityManager#merge method), however, I have a rather complicated object structure with multiple levels of embedded entities, so doing the above process would be rather inconvinient.
Cannot Doctrine automatically do this task for me? So instead of 'complaining' about my detached entities, it could automatically merge them.
As far as I can tell, you'd be creating some serious magic if you tried to make it entirely automatic.
Your best bet is probably to implement a function with a signature like: mergeOrderEntities($Order, $EntityManager) which knows how to talk the Order structure and invoke EntityManager::merge() on all detached associated entities.
As to why doctrine doesn't do this automatically, I'm not sure. I suspect there are use cases where auto-merging of entities could be dangerous, or at least undesirable.

Categories