From 515795e4a69c7639ffbab3e4523181d6b4d67de5 Mon Sep 17 00:00:00 2001 From: Andrej Hudec Date: Thu, 9 Jan 2020 08:51:17 +0100 Subject: [PATCH] Add `onMigrationsQueryExecuting` and `onMigrationsQueryExecuted` events --- docs/en/reference/events.rst | 15 ++ .../Event/MigrationsQueryEventArgs.php | 61 ++++++ lib/Doctrine/Migrations/EventDispatcher.php | 30 +++ lib/Doctrine/Migrations/Events.php | 2 + lib/Doctrine/Migrations/Query/Query.php | 48 +++++ .../Migrations/Version/DbalExecutor.php | 19 +- .../Migrations/Tests/Event/EventArgsTest.php | 13 ++ .../Migrations/Tests/Query/QueryTest.php | 20 ++ .../Migrations/Tests/Version/ExecutorTest.php | 201 ++++++++---------- 9 files changed, 300 insertions(+), 109 deletions(-) create mode 100644 lib/Doctrine/Migrations/Event/MigrationsQueryEventArgs.php create mode 100644 lib/Doctrine/Migrations/Query/Query.php create mode 100644 tests/Doctrine/Migrations/Tests/Query/QueryTest.php diff --git a/docs/en/reference/events.rst b/docs/en/reference/events.rst index 52568a5f46..ecf29b74f4 100644 --- a/docs/en/reference/events.rst +++ b/docs/en/reference/events.rst @@ -8,6 +8,8 @@ there are no versions to be executed. - ``onMigrationsVersionExecuting``: dispatched before a single version executes. - ``onMigrationsVersionExecuted``: dispatched after a single version executes. - ``onMigrationsVersionSkipped``: dispatched when a single version is skipped. +- ``onMigrationsQueryExecuting``: dispatched before a single query executes. +- ``onMigrationsQueryExecuted``: dispatched after a single query executes. - ``onMigrationsMigrated``: dispatched when all versions have been executed. All of these events are emitted via the DBAL connection's event manager. Here's an example event subscriber that @@ -19,6 +21,7 @@ listens for all possible migrations events. use Doctrine\Common\EventSubscriber; use Doctrine\Migrations\Event\MigrationsEventArgs; + use Doctrine\Migrations\Event\MigrationsQueryEventArgs; use Doctrine\Migrations\Event\MigrationsVersionEventArgs; use Doctrine\Migrations\Events; @@ -32,6 +35,8 @@ listens for all possible migrations events. Events::onMigrationsVersionExecuting, Events::onMigrationsVersionExecuted, Events::onMigrationsVersionSkipped, + Events::onMigrationsQueryExecuting, + Events::onMigrationsQueryExecuted, ]; } @@ -59,6 +64,16 @@ listens for all possible migrations events. { // ... } + + public function onMigrationsQueryExecuting(MigrationsQueryEventArgs $args) : void + { + // ... + } + + public function onMigrationsQueryExecuted(MigrationsQueryEventArgs $args) : void + { + // ... + } } To add an event subscriber to a connections event manager, use the ``Connection::getEventManager()`` method diff --git a/lib/Doctrine/Migrations/Event/MigrationsQueryEventArgs.php b/lib/Doctrine/Migrations/Event/MigrationsQueryEventArgs.php new file mode 100644 index 0000000000..22bde7b096 --- /dev/null +++ b/lib/Doctrine/Migrations/Event/MigrationsQueryEventArgs.php @@ -0,0 +1,61 @@ +connection = $connection; + $this->plan = $plan; + $this->migratorConfiguration = $migratorConfiguration; + $this->query = $query; + } + + public function getConnection() : Connection + { + return $this->connection; + } + + public function getPlan() : MigrationPlan + { + return $this->plan; + } + + public function getMigratorConfiguration() : MigratorConfiguration + { + return $this->migratorConfiguration; + } + + public function getQuery() : Query + { + return $this->query; + } +} diff --git a/lib/Doctrine/Migrations/EventDispatcher.php b/lib/Doctrine/Migrations/EventDispatcher.php index 16485b6ab2..04bdd510fa 100644 --- a/lib/Doctrine/Migrations/EventDispatcher.php +++ b/lib/Doctrine/Migrations/EventDispatcher.php @@ -8,9 +8,11 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; use Doctrine\Migrations\Event\MigrationsEventArgs; +use Doctrine\Migrations\Event\MigrationsQueryEventArgs; use Doctrine\Migrations\Event\MigrationsVersionEventArgs; use Doctrine\Migrations\Metadata\MigrationPlan; use Doctrine\Migrations\Metadata\MigrationPlanList; +use Doctrine\Migrations\Query\Query; /** * The EventDispatcher class is responsible for dispatching events internally that a user can listen for. @@ -54,6 +56,21 @@ public function dispatchVersionEvent( $this->dispatchEvent($eventName, $event); } + public function dispatchQueryExecuteEvent( + string $eventName, + MigrationPlan $plan, + MigratorConfiguration $migratorConfiguration, + Query $query + ) : void { + $event = $this->createMigrationsQueryEventArgs( + $plan, + $migratorConfiguration, + $query + ); + + $this->dispatchEvent($eventName, $event); + } + private function dispatchEvent(string $eventName, ?EventArgs $args = null) : void { $this->eventManager->dispatchEvent($eventName, $args); @@ -76,4 +93,17 @@ private function createMigrationsVersionEventArgs( $migratorConfiguration ); } + + private function createMigrationsQueryEventArgs( + MigrationPlan $plan, + MigratorConfiguration $migratorConfiguration, + Query $query + ) : MigrationsQueryEventArgs { + return new MigrationsQueryEventArgs( + $this->connection, + $plan, + $migratorConfiguration, + $query + ); + } } diff --git a/lib/Doctrine/Migrations/Events.php b/lib/Doctrine/Migrations/Events.php index 0d2feb9fcf..e3e57e6956 100644 --- a/lib/Doctrine/Migrations/Events.php +++ b/lib/Doctrine/Migrations/Events.php @@ -11,6 +11,8 @@ final class Events { public const onMigrationsMigrating = 'onMigrationsMigrating'; public const onMigrationsMigrated = 'onMigrationsMigrated'; + public const onMigrationsQueryExecuting = 'onMigrationsQueryExecuting'; + public const onMigrationsQueryExecuted = 'onMigrationsQueryExecuted'; public const onMigrationsVersionExecuting = 'onMigrationsVersionExecuting'; public const onMigrationsVersionExecuted = 'onMigrationsVersionExecuted'; public const onMigrationsVersionSkipped = 'onMigrationsVersionSkipped'; diff --git a/lib/Doctrine/Migrations/Query/Query.php b/lib/Doctrine/Migrations/Query/Query.php new file mode 100644 index 0000000000..30b02c4cea --- /dev/null +++ b/lib/Doctrine/Migrations/Query/Query.php @@ -0,0 +1,48 @@ +statement = $statement; + $this->parameters = $parameters; + $this->types = $types; + } + + public function getStatement() : string + { + return $this->statement; + } + + /** @return mixed[] */ + public function getParameters() : array + { + return $this->parameters; + } + + /** @return mixed[] */ + public function getTypes() : array + { + return $this->types; + } +} diff --git a/lib/Doctrine/Migrations/Version/DbalExecutor.php b/lib/Doctrine/Migrations/Version/DbalExecutor.php index 58d494a684..90f5b3fe42 100644 --- a/lib/Doctrine/Migrations/Version/DbalExecutor.php +++ b/lib/Doctrine/Migrations/Version/DbalExecutor.php @@ -16,6 +16,7 @@ use Doctrine\Migrations\MigratorConfiguration; use Doctrine\Migrations\ParameterFormatter; use Doctrine\Migrations\Provider\SchemaDiffProvider; +use Doctrine\Migrations\Query\Query; use Doctrine\Migrations\Stopwatch; use Doctrine\Migrations\Tools\BytesFormatter; use Psr\Log\LoggerInterface; @@ -206,7 +207,7 @@ private function executeMigration( if (count($this->sql) !== 0) { if (! $configuration->isDryRun()) { - $this->executeResult($configuration); + $this->executeResult($configuration, $plan); } else { foreach ($this->sql as $idx => $query) { $this->outputSqlQuery($idx, $query); @@ -324,9 +325,16 @@ private function logResult(Throwable $e, ExecutionResult $result, MigrationPlan } } - private function executeResult(MigratorConfiguration $configuration) : void + private function executeResult(MigratorConfiguration $configuration, MigrationPlan $plan) : void { foreach ($this->sql as $key => $query) { + $migrationQuery = new Query($query, $this->params[$key] ?? [], $this->types[$key] ?? []); + $this->dispatcher->dispatchQueryExecuteEvent( + Events::onMigrationsQueryExecuting, + $plan, + $configuration, + $migrationQuery + ); $stopwatchEvent = $this->stopwatch->start('query'); $this->outputSqlQuery($key, $query); @@ -339,6 +347,13 @@ private function executeResult(MigratorConfiguration $configuration) : void $stopwatchEvent->stop(); + $this->dispatcher->dispatchQueryExecuteEvent( + Events::onMigrationsQueryExecuted, + $plan, + $configuration, + $migrationQuery + ); + if (! $configuration->getTimeAllQueries()) { continue; } diff --git a/tests/Doctrine/Migrations/Tests/Event/EventArgsTest.php b/tests/Doctrine/Migrations/Tests/Event/EventArgsTest.php index 68a9e03d0f..4cbb7ef26f 100644 --- a/tests/Doctrine/Migrations/Tests/Event/EventArgsTest.php +++ b/tests/Doctrine/Migrations/Tests/Event/EventArgsTest.php @@ -7,10 +7,12 @@ use Doctrine\DBAL\Connection; use Doctrine\Migrations\AbstractMigration; use Doctrine\Migrations\Event\MigrationsEventArgs; +use Doctrine\Migrations\Event\MigrationsQueryEventArgs; use Doctrine\Migrations\Event\MigrationsVersionEventArgs; use Doctrine\Migrations\Metadata\MigrationPlan; use Doctrine\Migrations\Metadata\MigrationPlanList; use Doctrine\Migrations\MigratorConfiguration; +use Doctrine\Migrations\Query\Query; use Doctrine\Migrations\Version\Direction; use Doctrine\Migrations\Version\Version; use PHPUnit\Framework\MockObject\MockObject; @@ -44,6 +46,17 @@ public function testMigrationsVersionEventArgs() : void self::assertSame($this->plan, $event->getPlan()); } + public function testMigrationsQueryEventArgs() : void + { + $query = new Query('SELECT 1'); + $event = new MigrationsQueryEventArgs($this->connection, $this->plan, $this->config, $query); + + self::assertSame($this->connection, $event->getConnection()); + self::assertSame($this->config, $event->getMigratorConfiguration()); + self::assertSame($this->plan, $event->getPlan()); + self::assertSame($query, $event->getQuery()); + } + public function testMigrationsEventArgs() : void { $plan = new MigrationPlanList([], Direction::UP); diff --git a/tests/Doctrine/Migrations/Tests/Query/QueryTest.php b/tests/Doctrine/Migrations/Tests/Query/QueryTest.php new file mode 100644 index 0000000000..f5af18736e --- /dev/null +++ b/tests/Doctrine/Migrations/Tests/Query/QueryTest.php @@ -0,0 +1,20 @@ +getStatement()); + self::assertSame(['bar', 'baz'], $query->getParameters()); + self::assertSame(['qux', 'quux'], $query->getTypes()); + } +} diff --git a/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php b/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php index 89a71a273e..84e6345760 100644 --- a/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php +++ b/tests/Doctrine/Migrations/Tests/Version/ExecutorTest.php @@ -8,6 +8,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; +use Doctrine\Migrations\Event\MigrationsQueryEventArgs; use Doctrine\Migrations\EventDispatcher; use Doctrine\Migrations\Events; use Doctrine\Migrations\Metadata\MigrationPlan; @@ -27,6 +28,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Stopwatch\StopwatchEvent; use Throwable; +use function implode; class ExecutorTest extends TestCase { @@ -63,6 +65,9 @@ class ExecutorTest extends TestCase /** @var MockObject */ private $metadataStorage; + /** @var Listener */ + private $listener; + public function testAddSql() : void { $this->versionExecutor->addSql('SELECT 1', [1], [2]); @@ -180,32 +185,9 @@ public function testSkipMigration() : void $migratorConfiguration = (new MigratorConfiguration()) ->setTimeAllQueries(true); - $listener = new class() { - /** @var bool */ - public $onMigrationsVersionExecuting = false; - /** @var bool */ - public $onMigrationsVersionExecuted = false; - /** @var bool */ - public $onMigrationsVersionSkipped = false; - - public function onMigrationsVersionExecuting() : void - { - $this->onMigrationsVersionExecuting = true; - } - - public function onMigrationsVersionExecuted() : void - { - $this->onMigrationsVersionExecuted = true; - } - - public function onMigrationsVersionSkipped() : void - { - $this->onMigrationsVersionSkipped = true; - } - }; - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionSkipped, $listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionSkipped, $this->listener); $plan = new MigrationPlan($this->version, $this->migration, Direction::UP); $this->migration->skip = true; @@ -223,9 +205,11 @@ public function onMigrationsVersionSkipped() : void self::assertFalse($this->migration->preDownExecuted); self::assertFalse($this->migration->postDownExecuted); - self::assertFalse($listener->onMigrationsVersionExecuted); - self::assertTrue($listener->onMigrationsVersionSkipped); - self::assertTrue($listener->onMigrationsVersionExecuting); + self::assertFalse($this->listener->onMigrationsVersionExecuted); + self::assertTrue($this->listener->onMigrationsVersionSkipped); + self::assertTrue($this->listener->onMigrationsVersionExecuting); + self::assertFalse($this->listener->onMigrationsQueryExecuting); + self::assertFalse($this->listener->onMigrationsQueryExecuted); } /** @@ -238,31 +222,21 @@ public function testMigrationEvents() : void $plan = new MigrationPlan($this->version, $this->migration, Direction::UP); - $listener = new class() { - /** @var bool */ - public $onMigrationsVersionExecuting = false; - /** @var bool */ - public $onMigrationsVersionExecuted = false; - - public function onMigrationsVersionExecuting() : void - { - $this->onMigrationsVersionExecuting = true; - } - - public function onMigrationsVersionExecuted() : void - { - $this->onMigrationsVersionExecuted = true; - } - }; - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsQueryExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsQueryExecuted, $this->listener); $this->versionExecutor->execute( $plan, $migratorConfiguration ); - self::assertTrue($listener->onMigrationsVersionExecuted); - self::assertTrue($listener->onMigrationsVersionExecuting); + self::assertTrue($this->listener->onMigrationsVersionExecuted); + self::assertTrue($this->listener->onMigrationsVersionExecuting); + self::assertTrue($this->listener->onMigrationsQueryExecuting); + self::assertTrue($this->listener->onMigrationsQueryExecuted); + self::assertSame('SELECT 1;SELECT 2', implode(';', $this->listener->executingQueries)); + self::assertSame('SELECT 1;SELECT 2', implode(';', $this->listener->executedQueries)); } /** @@ -280,32 +254,11 @@ public function testErrorMigration() : void $plan = new MigrationPlan($this->version, $this->migration, Direction::UP); $this->migration->error = true; - $listener = new class() { - /** @var bool */ - public $onMigrationsVersionExecuting = false; - /** @var bool */ - public $onMigrationsVersionExecuted = false; - /** @var bool */ - public $onMigrationsVersionSkipped = false; - - public function onMigrationsVersionExecuting() : void - { - $this->onMigrationsVersionExecuting = true; - } - - public function onMigrationsVersionExecuted() : void - { - $this->onMigrationsVersionExecuted = true; - } - - public function onMigrationsVersionSkipped() : void - { - $this->onMigrationsVersionSkipped = true; - } - }; - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionSkipped, $listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionSkipped, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsQueryExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsQueryExecuted, $this->listener); $migrationSucceed = false; try { @@ -315,9 +268,11 @@ public function onMigrationsVersionSkipped() : void ); $migrationSucceed = true; } catch (Throwable $e) { - self::assertFalse($listener->onMigrationsVersionExecuted); - self::assertTrue($listener->onMigrationsVersionSkipped); - self::assertTrue($listener->onMigrationsVersionExecuting); + self::assertFalse($this->listener->onMigrationsVersionExecuted); + self::assertTrue($this->listener->onMigrationsVersionSkipped); + self::assertTrue($this->listener->onMigrationsVersionExecuting); + self::assertFalse($this->listener->onMigrationsQueryExecuting); + self::assertFalse($this->listener->onMigrationsQueryExecuted); $result = $plan->getResult(); self::assertNotNull($result); @@ -352,32 +307,11 @@ public function testChangesNotCommittedIfMetadataFailure() : void $plan = new MigrationPlan($this->version, $this->migration, Direction::UP); - $listener = new class() { - /** @var bool */ - public $onMigrationsVersionExecuting = false; - /** @var bool */ - public $onMigrationsVersionExecuted = false; - /** @var bool */ - public $onMigrationsVersionSkipped = false; - - public function onMigrationsVersionExecuting() : void - { - $this->onMigrationsVersionExecuting = true; - } - - public function onMigrationsVersionExecuted() : void - { - $this->onMigrationsVersionExecuted = true; - } - - public function onMigrationsVersionSkipped() : void - { - $this->onMigrationsVersionSkipped = true; - } - }; - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $listener); - $this->eventManager->addEventListener(Events::onMigrationsVersionSkipped, $listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionExecuted, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsVersionSkipped, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsQueryExecuting, $this->listener); + $this->eventManager->addEventListener(Events::onMigrationsQueryExecuted, $this->listener); $migrationSucceed = false; try { @@ -387,9 +321,13 @@ public function onMigrationsVersionSkipped() : void ); $migrationSucceed = true; } catch (Throwable $e) { - self::assertFalse($listener->onMigrationsVersionExecuted); - self::assertTrue($listener->onMigrationsVersionSkipped); - self::assertTrue($listener->onMigrationsVersionExecuting); + self::assertFalse($this->listener->onMigrationsVersionExecuted); + self::assertTrue($this->listener->onMigrationsVersionSkipped); + self::assertTrue($this->listener->onMigrationsVersionExecuting); + self::assertTrue($this->listener->onMigrationsQueryExecuting); + self::assertTrue($this->listener->onMigrationsQueryExecuted); + self::assertSame('SELECT 1;SELECT 2', implode(';', $this->listener->executingQueries)); + self::assertSame('SELECT 1;SELECT 2', implode(';', $this->listener->executedQueries)); $result = $plan->getResult(); self::assertNotNull($result); @@ -465,6 +403,55 @@ protected function setUp() : void $stopwatchEvent->expects(self::any()) ->method('getMemory') ->willReturn(100); + + $this->listener = new Listener(); + } +} + +class Listener +{ + /** @var bool */ + public $onMigrationsVersionExecuting = false; + /** @var bool */ + public $onMigrationsVersionExecuted = false; + /** @var bool */ + public $onMigrationsVersionSkipped = false; + /** @var bool */ + public $onMigrationsQueryExecuting = false; + /** @var bool */ + public $onMigrationsQueryExecuted = false; + /** @var string[] */ + public $executingQueries = []; + /** @var string[] */ + public $executedQueries = []; + + public function onMigrationsVersionExecuting() : void + { + $this->onMigrationsVersionExecuting = true; + } + + public function onMigrationsVersionExecuted() : void + { + $this->onMigrationsVersionExecuted = true; + } + + public function onMigrationsVersionSkipped() : void + { + $this->onMigrationsVersionSkipped = true; + } + + public function onMigrationsQueryExecuting(MigrationsQueryEventArgs $migrationsQueryEventArgs) : void + { + $this->onMigrationsQueryExecuting = true; + + $this->executingQueries[] = $migrationsQueryEventArgs->getQuery()->getStatement(); + } + + public function onMigrationsQueryExecuted(MigrationsQueryEventArgs $migrationsQueryEventArgs) : void + { + $this->onMigrationsQueryExecuted = true; + + $this->executedQueries[] = $migrationsQueryEventArgs->getQuery()->getStatement(); } }