Device
which has a bidirectional OneToMany relation with Attribute
entityThis is what happens:
Device
object from DB through Doctrine repository - the device has an Attribute
collection not yet initialized (lazy loaded)Proxy
but has __isInitialized__
set as true
attribute->setValue(true)
)Attribute
entity repository in order to check if its value has been properly changed in DB from the listenerThe problem comes at point 6: attribute's value is wrong.
Now someone might think it's normal since the object is already loaded and marked as initialized in Behat test so Doctrine is not aware of changes done by concurrent processes and this would fine.
However what is really strange is that Doctrine is executing anyway a query at point 6 (checked with Doctrine SQL logging) and this query is returning a result set which contains different values for that device attribute (ie. a different value
property) but the attribute variable is not automatically updated!
In fact in order to update it I have to call $em->refresh()
on the attribute or clear the cache using $em->clear()
before fetching attribute at point 6.
Is this a bug in Doctrine or what? Any thoughts?
I think I found the answer I was looking for.
Debugging through Doctrine classes (specifically in UnitOfWork::createEntity()
) I found out that Doctrine won't overwrite existing entity data unless the query explicitly specifies a Query::HINT_REFRESH
hint:
$result = $this->getEntityManager()->createQueryBuilder()
->select('e')
->from(MyEntity::class, 'e')
->getQuery()->setHint(Query::HINT_REFRESH, true)->getResult();
This way Doctrine will overwrite entity properties if there's new DB data available for this entity even if the entity was already loaded in memory. Moreover this doesn't fire a new query like $em->refresh()
does.
In fact this is also confirmed by official documentation:
Query::HINT_REFRESH - This query is used internally by EntityManager::refresh() and can be used in userland as well. If you specify this hint and a query returns the data for an entity that is already managed by the UnitOfWork, the fields of the existing entity will be refreshed. In normal operation a result-set that loads data of an already existing entity is discarded in favor of the already existing entity.