Search This Blog

Friday, October 3, 2014

Co-existing Doctrine 2 ORM with other database access methods.

This is useful if you are maintaiing legacy code that uses another ORM, but are adding new features with Doctrine 2.
A recent case I worked in was adding a 'free months' feature to an existing subscription purchasing system.
This post may also help you diagnose and resolve doctrine
 A new entity was found through the relationship...that was not configured to cascade persist operations
errors
The existing code was using ZendDb, and the application had the Doctrine 2 libraries, entity and repository classes created so it appeared to make sense to add the new features as business objects can call from from the existing code.
A problem arose when the existing code - having modified the transaction in question and saved the details to the database, but the changes were not reflected in the Doctrine entities, even though I was 'retrieving' them as required.
If course the entities were not being reread from the database but from the cache.
So I added the code to detach my entities:

/**
 * @var $oEm \Doctrine\ORM\EntityManager
 *
 */
$oEm = \Europa\ServiceLocator::getInstance()->get('doctrine2em');
$oEm->detach($oORMTransaction);
$oORMTransaction = null;

This worked well, when I next retrieved the entities I got correctly updated values.

However later in the code I called a new method:
$oSubscriptionTransRepo->spendFreeMonths($oTransaction);

In this any available free months are spent (i.e. decremented).
When this ran I got the error:
Message: A new entity was found through the relationship 
Message: A new entity was found through the relationship 'Viomedia\ORM\Entity\SubscriptionTransItem#CartTrans' that was not configured to cascade persist operations for entity: Viomedia\ORM\Entity\CartTrans@0000000055ca15a6000000001600931c. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist  this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'Viomedia\ORM\Entity\CartTrans#__toString()' to get a clue.
File: /var/webserver/viomedia/vendor/doctrine/orm/lib/Doctrine/ORM/ORMInvalidArgumentException.php:91
As detailed in stackExchange: especially the answer by Jasper N. Brouwer - paraphrasing for the code above: 
The exception says: A new entity was found through the relationship 'Viomedia\ORM\Entity\SubscriptionTransItem#CartTrans'.  So it's the value of $cartTrans in SubscriptionTransItem. I guess that would be a SubscriptionTransItem entity 
Like I said, the problem is probably not that the SubscriptionTransItem hasn't been persisted, but that it currently isn't managed by the entity-manager.  
What the detach did above was to leave the child SubscriptionTransItem entities in a un-managed state.
I fixed this (for now) by changing the above code to:
/**
 * @var $oEm \Doctrine\ORM\EntityManager
 *
 */
$oEm = \Europa\ServiceLocator::getInstance()->get('doctrine2em');
foreach(\Viomedia\ORM\Repository\SubscriptionTransItem::getInstance()->getSubscriptionTransItems($oORMTransaction) as $oTransItem)
 {
    $oEm->detach($oTransItem);
}
$oEm->detach($oORMTransaction);
$oORMTransaction = null;
The real solution however is to avoid mixing more than 1 entity manager in the same code.