diff --git a/src/Symfony/Bridge/Doctrine/CHANGELOG.md b/src/Symfony/Bridge/Doctrine/CHANGELOG.md index 3bcf9a77dfe5..3fdbe4fb1113 100644 --- a/src/Symfony/Bridge/Doctrine/CHANGELOG.md +++ b/src/Symfony/Bridge/Doctrine/CHANGELOG.md @@ -7,6 +7,8 @@ CHANGELOG * changed guessing of DECIMAL to set the `input` option of `NumberType` to string * deprecated not passing an `IdReader` to the `DoctrineChoiceLoader` when query can be optimized with a single id field * deprecated passing an `IdReader` to the `DoctrineChoiceLoader` when entities have a composite id + * added `DoctrinePingConnectionMiddleware` + * added `DoctrineCloseConnectionMiddleware` 4.2.0 ----- diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php new file mode 100644 index 000000000000..6520ac0a3595 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrineCloseConnectionMiddleware.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Middleware\StackInterface; + +/** + * Closes connection and therefore saves number of connections. + * + * @author Fuong + * + * @experimental in 4.3 + */ +class DoctrineCloseConnectionMiddleware implements MiddlewareInterface +{ + private $managerRegistry; + private $entityManagerName; + + public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) + { + $this->managerRegistry = $managerRegistry; + $this->entityManagerName = $entityManagerName; + } + + /** + * {@inheritdoc} + */ + public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + $entityManager = $this->managerRegistry->getManager($this->entityManagerName); + + if (!$entityManager instanceof EntityManagerInterface) { + throw new \InvalidArgumentException(sprintf('The ObjectManager with name "%s" must be an instance of EntityManagerInterface', $this->entityManagerName)); + } + + try { + $connection = $entityManager->getConnection(); + + return $stack->next()->handle($envelope, $stack); + } finally { + $connection->close(); + } + } +} diff --git a/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php new file mode 100644 index 000000000000..021d7a839206 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Messenger/DoctrinePingConnectionMiddleware.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Middleware\MiddlewareInterface; +use Symfony\Component\Messenger\Middleware\StackInterface; + +/** + * Checks whether the connection is still open or reconnects otherwise. + * + * @author Fuong + * + * @experimental in 4.3 + */ +class DoctrinePingConnectionMiddleware implements MiddlewareInterface +{ + private $managerRegistry; + private $entityManagerName; + + public function __construct(ManagerRegistry $managerRegistry, string $entityManagerName = null) + { + $this->managerRegistry = $managerRegistry; + $this->entityManagerName = $entityManagerName; + } + + /** + * {@inheritdoc} + */ + public function handle(Envelope $envelope, StackInterface $stack): Envelope + { + $entityManager = $this->managerRegistry->getManager($this->entityManagerName); + + if (!$entityManager instanceof EntityManagerInterface) { + throw new \InvalidArgumentException(sprintf('The ObjectManager with name "%s" must be an instance of EntityManagerInterface', $this->entityManagerName)); + } + + $connection = $entityManager->getConnection(); + + if (!$connection->ping()) { + $connection->close(); + $connection->connect(); + } + + if (!$entityManager->isOpen()) { + $this->managerRegistry->resetManager($this->entityManagerName); + } + + return $stack->next()->handle($envelope, $stack); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineCloseConnectionMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineCloseConnectionMiddlewareTest.php new file mode 100644 index 000000000000..3036b4259340 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrineCloseConnectionMiddlewareTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Doctrine\Messenger\DoctrineCloseConnectionMiddleware; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; + +class DoctrineCloseConnectionMiddlewareTest extends MiddlewareTestCase +{ + private $connection; + private $entityManager; + private $managerRegistry; + private $middleware; + private $entityManagerName = 'default'; + + protected function setUp() + { + $this->connection = $this->createMock(Connection::class); + + $this->entityManager = $this->createMock(EntityManagerInterface::class); + $this->entityManager->method('getConnection')->willReturn($this->connection); + + $this->managerRegistry = $this->createMock(ManagerRegistry::class); + $this->managerRegistry->method('getManager')->willReturn($this->entityManager); + + $this->middleware = new DoctrineCloseConnectionMiddleware( + $this->managerRegistry, + $this->entityManagerName + ); + } + + public function testMiddlewareCloseConnection() + { + $this->connection->expects($this->once()) + ->method('close') + ; + + $this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php new file mode 100644 index 000000000000..cc1562522798 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Messenger/DoctrinePingConnectionMiddlewareTest.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Messenger; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\DBAL\Connection; +use Doctrine\ORM\EntityManagerInterface; +use Symfony\Bridge\Doctrine\Messenger\DoctrinePingConnectionMiddleware; +use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\Test\Middleware\MiddlewareTestCase; + +class DoctrinePingConnectionMiddlewareTest extends MiddlewareTestCase +{ + private $connection; + private $entityManager; + private $managerRegistry; + private $middleware; + private $entityManagerName = 'default'; + + protected function setUp() + { + $this->connection = $this->createMock(Connection::class); + + $this->entityManager = $this->createMock(EntityManagerInterface::class); + $this->entityManager->method('getConnection')->willReturn($this->connection); + + $this->managerRegistry = $this->createMock(ManagerRegistry::class); + $this->managerRegistry->method('getManager')->willReturn($this->entityManager); + + $this->middleware = new DoctrinePingConnectionMiddleware( + $this->managerRegistry, + $this->entityManagerName + ); + } + + public function testMiddlewarePingOk() + { + $this->connection->expects($this->once()) + ->method('ping') + ->willReturn(false); + + $this->connection->expects($this->once()) + ->method('close') + ; + $this->connection->expects($this->once()) + ->method('connect') + ; + + $this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } + + public function testMiddlewarePingResetEntityManager() + { + $this->entityManager->expects($this->once()) + ->method('isOpen') + ->willReturn(false) + ; + $this->managerRegistry->expects($this->once()) + ->method('resetManager') + ->with($this->entityManagerName) + ; + + $this->middleware->handle(new Envelope(new \stdClass()), $this->getStackMock()); + } +}