I am trying to do some processing within a transaction and save information about potential failure, much like this:
$conn->beginTransaction();
try
{
$report = $reportRepository->find($id);
$user = $report->getUser();
$specification = new Specification();
$entityManager->persist($specification);
throw new ProcessingWentWrongException();
$entityManager->flush();
$conn->commit();
}
catch(ProcessingWentWrongException $e)
{
$conn->rollback();
// Store error info:
$report->setState('error');
$entityManager->persist($report);
$entityManager->flush(); // all hell breaks loose in here
}
This looks like a really common pattern, but Doctrine makes it really hard to do it:
flush
in the catch{}
section will try to persist both the $report
and $specification
object which obviously is wrong, so I could clear
the entityManager
, but then...
If I clear
the entityManager
, $report
is no longer managed by it, so I need to call $em->merge($report)
to make it managed again. Obviously $user
will stay unmanaged so doctrine will either perform an insert
or complain about persist cascade
. So I can either merge()
the whole graph (which sucks) or close
the entityManager, but then...
If I close
the entityManager
I can only re-retrieve the report
instance via $repo->find($id);
- but I don't want to do that, it's stupid.
Did I miss anything? Is there some other way to achieve the result above? I feel like Doctrine makes easy things hard.
Use two entitymanagers. One for the potentially unsafe operations and one for logging/reporting on the other.
Generally, you cannot make sure that errors don't happen (some errors don't happen before you flush to the database). And once they happen, the entitymanager is closed for business.
Here's what I do (excerpts from config.yml):
doctrine:
orm:
default_entity_manager: 'default'
entity_managers:
default:
mappings: { ... }
logging:
mappings: { ... }
For normal operations, I use the default entity manager, which requires no change to your code.
For meta-operations (like logging the progress or result of a batched import or something similar), I explicitly fetch the 'logging'
manager and use it for creating/updating the logging/report entities (and only for those).
In this particular example you are adding specification to Report
. So can you this this?
$entityManager->clear("Your\Bundle\Entity\Specification");
and then do as you proposed:
// Store error info:
$report->setState('error');
$entityManager->persist($report);
$entityManager->flush(); // all hell breaks loose in here
Also, I think doing persist
on object with assigned ID is invalid. ( $report
object in catch
branch)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With