This has been bugging me for a while.
In Doctrine2, we have the: ObjectManager function:
void persist(object $object = null)
You only need to call it on new entities.
My question though, is "when" should it be called? Immediately after creating the entity, or immediately before flushing it?
I can't find any documentation indicating the convention. The reason this is important is because Doctrine dispatches the "persist event" when calling.
Given that the object might still be empty, it seems to imply that any functionality tagged on to that event should disregard the importance of the data the object contains at that point in time.
Am I correct in that statement or is there a convention Doctrine promotes?
What you want to do is create your new object, use it anyway you want, and when you're done with it and want to send it to your database, then persist it, just before flushing it.
If you persisted your entity just after creating it, any changes you would make wouldn't be taken into account when sent to the database.
Related
Short and easy question:
How to determine that Doctrine PHP Entity Object is new?
New - I mean is not in database and its not "update" action.
Most important thing - I need way to check it without any query like:
$manager->findOne($something);
Actually sometimes I tried to check "is ID null" but Im not 100% sure this method is valid or not. And other tip or probably core of this question - I seen something like:
$manager->getUnitOfWork()->isInIdentityMap($object);
Looks nice but I can't find what actually do function isInIdentityMap.
Is it true if it was persisted or removed? When this function say true and false?
//EDIT
I need to detect entity object created first time - existing only in php but not in database. Of course entity loaded from database to apply updates is not new for me - for example:
When you createing Comment object you need to increment commentCounter but it should be incremented only when you flushing this comment for first time.
You don't want to increment it when you updating or deleting existing comment.
How to check that comment is new and counter should be incremented.
Please explain in answers why your method is good/better
You can use
$entityManager->contains($entity)
Edit:
Based on your edit, i suggest to use an entity listener:
http://doctrine-orm.readthedocs.io/en/latest/reference/events.html#entity-listeners
So you don't need to check if the entity is new or not in many parts in your application when an entity is persisted, you only setup the entity listener and it works wherever you create and persist the entities.
I checked some docs and tested some things and I hope that everything I write is correct:
Method isInIdentityMap
$manager->getUnitOfWork()->isInIdentityMap($object);
is probably perfect for my case because it checks that my object exist in Identity Map, this map store only flushed objects or actually object with ID. New Object before persistence and after persist action still have NULL ID so its not in this map. This way as long as object was not flushed into database this method should work. (Map contains only objects loaded from database)
Method to check NULL ID
if($obj->getId() == null){
//code
}
should work too, its very similar to first methid but still Im not 100% sure it always work as I want and in addition we have first method so... Its better to use function prepared for it.
Method contains
$entityManager->contains($entity)
Do job but for other case. This method checks objects in entity manager but any persisted object, scheduled for any action is already tracked by entityManager. This function can help only as long as object is not persisted.
Other methods
Unit of work have many other methods, I didnt check them all but it seems we can easly check many other things using functions like:
->scheduleForInsert();
->scheduleForUpdate();
// etc
And other case - using Doctrine events event:
prePersist
prePersist - The prePersist event occurs for a given entity before the respective EntityManager persist operation for that entity is executed. It should be noted that this event is only triggered on initial persist of an entity (i.e. it does not trigger on future updates).
I have a class (PersistenceClass), that takes an array of data (posts) and parses that data and puts it into a DB (via doctrine). The field content needs to be parsed by a second class (SyntaxClass) before it is set into the doctrine entity.
Now the problem is, that the SyntaxClass has to set references in the content to other posts (just a link with and ID). So it needs access to the DB, and also needs to search in the persisted but not yet flushed entities from the PersistenceClass.
I can inject a doctrine EM into SyntaxClass and find my references in DB, although I dont like it very much. But the bigger problem is, how I can access the only persisted, but not flushed entities from the PersistenceClass ? I could make an Array of that objects and put it as an parameter to the parser method like:
SyntaxClass->parseSyntax($content, $persistedObjects);
But that does not look very clean. Aside from that, I dont know if it is somehow possible to search in the data of the persisted objects?
Your question is full of sub-question, so, first I'll try to make some things clear.
First, the naming convention you used is a bit abiguos and this not helps, me and also other people that may work on your code in future (maybe you'll grow and need to hire more developers! :P ). So, let's start with some nomenclature.
What you are calling PersistenceClass may be something like this:
class PersistenceClass
{
public function parse(array $posts)
{
foreach ($posts as $post) {
// 1. Parse $post
// 2. Parse content with SyntaxClass
// 3. Persist $post in the database
}
}
}
The same applies also for SyntaxClass: it receives the $content and parses it in some ways, then sets the references and then persists.
This is just to set some boundaries.
Now, go to your questions.
I can inject a doctrine EM into SyntaxClass and find my references in
DB, although I dont like it very much.
This is exactly what you have to do! The OOP development works this way.
But, and here come the problems with naming conventions, the way you inject the entity manager depends on the structure of your classes.
A good design should use services.
So, what currently are PersistenceClass and SyntaxClass in reality should be called PersistenceService and SyntaxService (also if I prefere call them PersistenceManager and SyntaxManager, because in my code I always distinguish between managers and handlers - but this is a convention of mine, so I'll not write more about it here).
Now, another wrong thing that I'm imaging you are doing (only reading your question, I'M IMAGING!): you are instantiating SyntaxService (you currently named SyntaxClass) from inside PersistenceService (your currently named PersistenceClass). This is wrong.
If you need a fresh instance of SyntaxService for each post, then you should use a factory class (say SyntaxFactory), so calling SyntaxFactory::create() you'll get a fresh instance of SyntaxService. Is the factory itself that injects the entity manager in the newly created SyntaxClass.
If you don't need a fresh instance each, time, instead, you'll declare SyntaxClass simply as a service and will pass it to PersistenceService by injection. Below this last simpler example:
# app/config/service.yml
services:
app.service.persistence:
class: ...\PersistenceService
# Pass the SyntaxInstance directly or a factory if you need one
aguments: ["#doctrine.orm.default_entity_manager", "#app.service.syntax"]
app.service.syntax:
class: ...\SyntaxService
aguments: ["#doctrine.orm.default_entity_manager"]
But the bigger problem is, how I can access the only persisted, but
not flushed entities from the PersistenceClass ?
Now the second question: how to search for {persisted + flushed} and {persisted + not flushed} entities?
The problem is that you cannot use the ID as the search parameter as the persisted but not flushed entities doesn't have one before the flushing.
The solution may be to create another service: SearchReferencesService. In it you'll inject the entity manager too (as shown before).
So this class has a method search() that does the search.
To search for the entities persisted but not flushed, the UnitOfWork gives you some interesting methods: getScheduledEntityInsertions(), getScheduledEntityUpdates(), getScheduledEntityDeletions(), getScheduledCollectionDeletions() and getScheduledCollectionUpdates().
The array of which you are speaking about is already there: you need to only cycle it and compare object by object, basing the search on fields other than the ID one (as it doesn't exist yet).
Unfortunately, as you didn't provided more details about the nature of your search, it is not possible for me to be more precise about how to do this search, but only tell you you have to search using the unit of work and connecting to the database if null results are returned by the first search. Also the order in which you'll do this search (before in the database and then in the unit of work or viceversa) is up to you.
Hope this will help.
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 :-)
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.
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.