/** * @return EntityManagerInterface */ public function getEm() { return $this->context->getEm(); }
/** * @inheritdoc */ public function intercept(MethodInvocation $invocation) { $metadata = $this->findMethodMetadata(get_class($invocation->object), $invocation->reflection->name); $em = $this->doctrine->getManager($metadata->getEm()); if (!$em instanceof EntityManagerInterface) { throw new \Exception('Wrong object manager class'); } $context = new TransactionalInvocationContext($em); $arguments = []; foreach ($invocation->reflection->getParameters() as $i => $param) { $arguments[$param->name] = $invocation->arguments[$i]; } try { $em->beginTransaction(); /** @see https://www.percona.com/blog/2012/08/28/differences-between-read-committed-and-repeatable-read-transaction-isolation-levels/ */ /** @see http://www.ovaistariq.net/597/understanding-innodb-transaction-isolation-levels/ */ $em->getConnection()->setTransactionIsolation(Connection::TRANSACTION_READ_COMMITTED); array_push($invocation->arguments, $context); $ret = $invocation->proceed(); $em->flush(); if (!$em->isOpen() || $em->getConnection()->isRollbackOnly()) { throw new \Exception('Rollback invoked'); } $em->commit(); if ($metadata->getOnSuccess() && !$context->isPreventSuccess()) { $this->invokeEvent($invocation->object, $metadata->getOnSuccess(), new TransactionalSuccessEvent($arguments, $context, $ret)); } return $ret; } catch (\Exception $e) { if ($em->getConnection()->isTransactionActive()) { // TODO: postgresql, savepoints if (!$e instanceof DBALException && !$e instanceof \PDOException && $em->isOpen() && !$em->getConnection()->isRollbackOnly()) { try { $em->flush(); } catch (\Exception $e) { // TODO: log? stop catching? } } $em->rollback(); } if (!$em->isOpen()) { $this->doctrine->resetManager($metadata->getEm()); $contextOld = $context; /** @var EntityManagerInterface $em */ $em = $this->doctrine->getManager($metadata->getEm()); $context = new TransactionalInvocationContext($em); $context->setData($contextOld->getData()); } if ($metadata->getOnError()) { return $this->invokeEvent($invocation->object, $metadata->getOnError(), new TransactionalErrorEvent($arguments, $context, $e)); } throw $e; } }