The doctrine interface Doctrine\Common\Persistence\ObjectManager defines the flush method as having no parameters. Yet the implementation Doctrine\ORM\EntityManager allows a single entity to be passed.
Aside from the, IMO, bad programming style, Is this anything I need to be worried about?
I know that PHP will ignore any extra parameters if they are not declared in the method. Which would cause a non ORM manager to flush all entities.
I ask because I'm trying to write my code in such a way that the ORM is configurable and can switched at a later date. Now, while writing a batch import class, I have found that calling flush without an entity causes memory leaks, it also effects a 'progress/history' entity I use outside of the main import loop. So it's pretty important I only flush certain entities.
I have noticed the differences between the definition and implementation of flush() as well. That may be a question only the developers of doctrine can answer.
Short Answer
Don't worry about it.
Long Answer
We can still address the differences and how they affect your application.
According to doctrine's documentation, flush() is the only method that will persist changes to your domain objects.
Other methods, such as persist() and remove() only place that object in a queue to be updated.
It is very important to understand that only EntityManager#flush() ever causes write operations against the database to be executed. Any other methods such as EntityManager#persist($entity) or EntityManager#remove($entity) only notify the UnitOfWork to perform these operations during flush.
Not calling EntityManager#flush() will lead to all changes during that request being lost.
Performance
Flushing individual entities at a time may cause performance issues in itself. Each flush() is a new trip to the database. Large sums of calls to flush() may slow down your application.
The flush() method should not be affecting your progress/history entity unless you are intentionally making changes to it. But, if that is the case, and you still do not want progress/history entity to be updated when flush() is executed, you can detach the entity from doctrine. This will allow you to make changes to the entity without doctrine being aware of those changes. Therefore, it will not be affected by flush().
When you are ready for the entity to be re-attached to doctrine, you can use the merge method provided by your entity manager. Then call flush() one last time to merge the changes.
Related
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.
I have a logger which use Doctrine to writes in DB. And I need log some events in Doctrine PostPersist, PostUpdate and PreRemove handlers of an Entity Listener.
But a flush operation is officialy unsupported (and sometimes cause fatal errors if neglect it) in these handlers.
I have encountered a similar question, but the solution is to defer a flush to the end of a currently performing flush, which is not suit me, cause I need to insert entry right in the handlers, e.g. in order not to lose object id during remove operation.
I have a LogManager and want that this add-log-entry operation be the same - no matter whether you invoke it from some handler or from some another place in code.
I wonder is there a way to persist objects in the handlers? (May be by using another EntityManager ...)
You can use onFlush event that I think suites well to your needs. Instead of using flush method you have to recompute/compute changes manually. See the link.
From Doctrine2 documentation:
If you create and persist a new entity in onFlush, then calling EntityManager#persist() is not enough. You have to execute an additional call to $unitOfWork->computeChangeSet($classMetadata, $entity).
Changing primitive fields or associations requires you to explicitly trigger a re-computation of the changeset of the affected entity. This can be done by calling $unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity).
Empirically it seems that flush() is not necessary after findAndUpdate(), I just couldn't find this explicitly stated anywhere in the Doctrine ODM/MongoDB docs (and I didn't bother to read much source code).
The findAndModify docs on mongodb.org state
This command can be used to atomically modify a document (at most one) and return it.
And Doctrine MongoDB's findAndUpdate() uses MongoDB's findAndModify. So it sounds like the whole thing does indeed happen in one go, so calling flush() on the document manager shouldn't be necessary.
Flush is only needed for writing changes to managed objects back to Mongo. Anything you do through the query builder interface will be executed directly and bypass UnitOfWork. This is especially true for updates and upserts. In the case of findAndUpdate(), the update should be executed in Mongo immediately, but I believe the object returned might be managed. Any changes to that document afterwards (e.g. via setter methods) would need a flush() if you wanted them written back to Mongo.
Also, be aware of returnNew() on the query builder, which corresponds to the new option of findAndModify. By default, I believe findAndUpdate() will return the document in its pre-updated state. You may prefer to retrieve the document in its updated state.
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.
I'm stuck with a general OOP problem, and can't find the right way to phrase my question.
I want to create a class that gives me an object which I can write to once, persist it to storage and then be unable to change the properties. (for example: invoice information - once written to storage, this should be immutable). Not all information is available immediately, during the lifecycle of the object, information is added.
What I'd like to avoid is having exceptions flying out of setters when trying to write, because it feels like you're offering a contract you don't intend to keep.
Here are some ideas I've considered so far:
Pass in any write-information in the constructor. Constructor throws exception if the data is already present.
Create multiple classes in an inheritance tree, with each class representing the entity at some stage of its lifecycle, with appropriate setters where needed. Add a colletive interface for all the read operations.
Silently discarding any inappropriate writes.
My thoughts on these:
1. Makes the constructor highly unstable, generally a bad idea.
2. Explosion of complexity, and doesn't solve the problem completely (you can call the setter twice in a row, within the same request)
3. Easy, but same problem as with the exceptions; it's all a big deception towards your clients.
(Just FYI: I'm working in PHP5 at the moment - although I suspect this to be a generic problem)
Interesting problem. I think your best choice was #1, but I'm not sure I'd do it in the constructor. That way the client code can choose what it wants to do with the exception (suppress them, handle them, pass them up to the caller, etc...). And if you don't like exceptions, you could move the writing to a write() method that returns true if the write was successful and false otherwise.