diff --git a/README.md b/README.md index b6fde00..1bc02c3 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ Worker classes are the classes responsible for actually performing the queries y ## Loggers -Loggers take care of registering the migrations that have been run already. This is to ensure that no migrations are being run more than once. By default, there is a database based Logger available, in the `CoenJacobs\Migrator\Loggers\DatabaseLogger` class. This class actually uses the aforementioned `$wpdb` based Worker, in order to log the migration data into a specific database table. You can provide your own implementation of the Logger class, as long as they implement the `CoenJacobs\Migrator\Contracts\Logger` interface. +Loggers take care of registering the migrations that have been run already. This is to ensure that no migrations are being run more than once. By default, there is a database based Logger available, in the `CoenJacobs\Migrator\Loggers\DatabaseLogger` class. This class actually uses the aforementioned `$wpdb` based Worker, in order to log the migration data into a specific database table. This database table is being created, using the table name you've provided as the first contructor argument. + +You can provide your own implementation of the Logger class, as long as they implement the `CoenJacobs\Migrator\Contracts\Logger` interface. ## Migration structure @@ -77,7 +79,8 @@ use CoenJacobs\Migrator\Loggers\DatabaseLogger; use CoenJacobs\Migrator\Workers\WpdbWorker; $worker = new WpdbWorker(); -$migrator = new Handler($worker, new DatabaseLogger()); +$logger = new DatabaseLogger('migrations_table_name'); +$handler = new Handler($worker, $logger); ``` After that, the Handler is ready to accept new migrations to be added, before they can be run. You pass the class as a string of the class name, where the class itself again implements the `CoenJacobs\Migrator\Contracts\Migration` interface: diff --git a/phpunit.xml b/phpunit.xml index e6b6f40..6bf6152 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,7 @@ - ./tests/unit/ + ./tests/Unit/ diff --git a/src/Exceptions/ReservedNameException.php b/src/Exceptions/ReservedNameException.php deleted file mode 100644 index 9d3a9ac..0000000 --- a/src/Exceptions/ReservedNameException.php +++ /dev/null @@ -1,9 +0,0 @@ -worker = $worker; $this->logger = $logger; $this->logger->setWorker($this->worker); - - $this->setupCoreMigrations(); - } - - private function setupCoreMigrations() - { - $this->add('core', CreateMigrationsTable::class); - - // From here on, the 'core' index as $plugin_key is a reserved name and - // can't be used by anyone else. - $this->reservedNames = [ - 'core' - ]; - } - - /** - * @param string $pluginKey - * @return bool - */ - public function isReservedName($pluginKey) - { - return in_array($pluginKey, $this->reservedNames); } /** * @param string $pluginKey * @param $migrationClassName - * @throws ReservedNameException */ public function add($pluginKey, $migrationClassName) { - if ($this->isReservedName($pluginKey)) { - throw new ReservedNameException($pluginKey . ' is a reserved name and can not be used by implementations.'); - } - $this->migrations[ $pluginKey ][] = $migrationClassName; } @@ -74,29 +45,17 @@ public function add($pluginKey, $migrationClassName) * Core migrations will always be run first to setup base tables for logging. * * @param string $pluginKey - * @throws ReservedNameException */ public function up($pluginKey) { - if ($this->isReservedName($pluginKey)) { - throw new ReservedNameException($pluginKey . ' is a reserved name and can not be used by implementations.'); - } - if (! isset($this->migrations[ $pluginKey ])) { return; } - $runMigrations = $this->logger->getLoggedMigrations(['core', $pluginKey]); + $runMigrations = $this->logger->getLoggedMigrations([$pluginKey]); $migrationsToRun = []; - // Add core migrations first - foreach ($this->migrations[ 'core' ] as $migrationClass) { - if (! in_array($migrationClass::id(), $runMigrations)) { - $migrationsToRun['core'][] = new $migrationClass($this->worker); - } - } - // Add added migrations for $pluginKey second foreach ($this->migrations[ $pluginKey ] as $migrationClass) { if (! in_array($migrationClass::id(), $runMigrations)) { @@ -112,14 +71,9 @@ public function up($pluginKey) * Core migrations will not be reversed since they can still be used by another plugin. * * @param string $pluginKey - * @throws ReservedNameException */ public function down($pluginKey) { - if ($this->isReservedName($pluginKey)) { - throw new ReservedNameException($pluginKey . ' is a reserved name and can not be used by implementations.'); - } - if (! isset($this->migrations[ $pluginKey ])) { return; } diff --git a/src/Loggers/DatabaseLogger.php b/src/Loggers/DatabaseLogger.php index 3c44190..88d640b 100644 --- a/src/Loggers/DatabaseLogger.php +++ b/src/Loggers/DatabaseLogger.php @@ -3,48 +3,59 @@ namespace CoenJacobs\Migrator\Loggers; use CoenJacobs\Migrator\Contracts\Migration; +use CoenJacobs\Migrator\Migrations\CreateMigrationsTable; class DatabaseLogger extends BaseLogger { + /** @var string */ + protected $tableName; + + /** @var bool */ + protected $setup = false; + + public function __construct($tableName) + { + $this->tableName = $tableName; + } + + public function init() + { + if (!$this->isTableSetup()) { + $migration = new CreateMigrationsTable($this->worker); + $migration->setTableName($this->tableName); + $migration->up(); + + $this->setup = true; + } + } + public function add($plugin_key, Migration $migration, $batch) { + $this->init(); $id = $migration->id(); - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; $batch = intval($batch); - $query = "INSERT INTO $tableName (migration, plugin_key, batch) + $query = "INSERT INTO $this->tableName (migration, plugin_key, batch) VALUES ('$id', '$plugin_key', '$batch')"; $this->worker->query($query); } public function remove($plugin_key, Migration $migration) { + $this->init(); $id = $migration->id(); - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; - $query = "DELETE FROM $tableName (migration, plugin_key) + $query = "DELETE FROM $this->tableName (migration, plugin_key) VALUES ('$id', '$plugin_key')"; $this->worker->query($query); } public function getLoggedMigrations($plugin_keys) { - $databaseName = $this->worker->getDatabaseName(); - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; + $this->init(); - // Check if table exists before we try to query it - $query = "SELECT count(*) - FROM information_schema.TABLES - WHERE (TABLE_SCHEMA = '$databaseName') AND (TABLE_NAME = '$tableName')"; - - $result = $this->worker->getResults($query); - - if (empty($result) || $result[0]->{"count(*)"} == 0) { - return []; - } - - $query = 'SELECT migration FROM '.$tableName.' - WHERE plugin_key IN ("'. implode('","', $plugin_keys) .'")'; + $query = 'SELECT migration FROM ' . $this->tableName . ' + WHERE plugin_key IN ("' . implode('","', $plugin_keys) . '")'; $results = $this->worker->getResults($query); @@ -53,33 +64,44 @@ public function getLoggedMigrations($plugin_keys) foreach ($results as $result) { $migrations[] = $result->migration; } + return $migrations; } public function getHighestBatchNumber() { + $this->init(); + + $query = 'SELECT MAX(batch) AS batch FROM ' . $this->tableName . ';'; + $results = $this->worker->getResults($query); + + if (empty($results)) { + return 0; + } + + return array_pop($results)->batch; + } + + protected function isTableSetup() + { + if ($this->setup === true) { + return true; + } + $databaseName = $this->worker->getDatabaseName(); - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; // Check if table exists before we try to query it $query = "SELECT count(*) FROM information_schema.TABLES - WHERE (TABLE_SCHEMA = '$databaseName') AND (TABLE_NAME = '$tableName')"; + WHERE (TABLE_SCHEMA = '$databaseName') AND (TABLE_NAME = '$this->tableName')"; $result = $this->worker->getResults($query); if (empty($result) || $result[0]->{"count(*)"} == 0) { - return 0; - } - - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; - $query = 'SELECT MAX(batch) AS batch FROM '.$tableName.';'; - $results = $this->worker->getResults($query); - - if (empty($results)) { - return 0; + return false; } - return array_pop($results)->batch; + $this->setup = true; + return true; } } diff --git a/src/Migrations/CreateMigrationsTable.php b/src/Migrations/CreateMigrationsTable.php index 41d6286..e25c494 100644 --- a/src/Migrations/CreateMigrationsTable.php +++ b/src/Migrations/CreateMigrationsTable.php @@ -4,16 +4,22 @@ class CreateMigrationsTable extends BaseMigration { + /** @var string */ + protected $tableName; + public static function id() { return 'migrator-1-migrations-table'; } - public function up() + public function setTableName($tableName) { - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; + $this->tableName = $tableName; + } - $query = "CREATE TABLE $tableName ( + public function up() + { + $query = "CREATE TABLE $this->tableName ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, migration VARCHAR(255) NOT NULL, plugin_key VARCHAR(255) NOT NULL, @@ -24,9 +30,7 @@ public function up() public function down() { - $tableName = $this->worker->getPrefix() . 'migrator_migrations'; - - $query = "DROP TABLE $tableName"; + $query = "DROP TABLE $this->tableName"; $this->worker->query($query); } } diff --git a/tests/Unit/HandlerTest.php b/tests/Unit/HandlerTest.php index d98895a..6389696 100644 --- a/tests/Unit/HandlerTest.php +++ b/tests/Unit/HandlerTest.php @@ -2,98 +2,81 @@ namespace CoenJacobs\MigratorTests\Unit; +use CoenJacobs\Migrator\Contracts\Migration as MigrationContract; use CoenJacobs\Migrator\Handler; -use CoenJacobs\Migrator\Contracts\Logger; -use CoenJacobs\Migrator\Contracts\Worker; -use CoenJacobs\Migrator\Contracts\Migration; -use CoenJacobs\Migrator\Exceptions\ReservedNameException; -use PHPUnit_Framework_TestCase; +use CoenJacobs\Migrator\Loggers\BaseLogger; +use CoenJacobs\Migrator\Migrations\BaseMigration; +use CoenJacobs\Migrator\Workers\BaseWorker; +use PHPUnit\Framework\TestCase; -class HandlerTest extends PHPUnit_Framework_TestCase +class HandlerTest extends TestCase { - private $worker; - private $logger; - private $downLogger; - private $migration; - - public function setUp() + /** @test */ + public function callsUpOnMigration() { - $this->worker = $this->getMockBuilder(Worker::class) - ->getMock(); - - $this->logger = $this->getMockBuilder(Logger::class) - ->getMock(); - $this->logger->expects($this->any()) - ->method('getLoggedMigrations') - ->will($this->returnValue([])); + $this->expectExceptionMessage('up method called'); - $this->downLogger = $this->getMockBuilder(Logger::class) - ->getMock(); - $this->downLogger->expects($this->any()) - ->method('getLoggedMigrations') - ->will($this->returnValue(['test-migration'])); - - $this->migration = $this->getMockBuilder(Migration::class) - ->getMock(); - $this->migration->expects($this->any()) - ->method('getId') - ->will($this->returnValue('test-migration')); + $handler = new Handler(new Worker(), new Logger()); + $handler->add('test-up-migrations', Migration::class); + $handler->up('test-up-migrations'); } /** @test */ - public function testHandlerCallsUpOnMigration() + public function callsDownOnMigration() { - $this->migration->expects($this->once())->method('up'); + $this->expectExceptionMessage('down method called'); - $handler = new Handler($this->worker, $this->logger); - $handler->add('test', $this->migration); - $handler->up('test'); + $handler = new Handler(new Worker(), new Logger()); + $handler->add('test-down-migrations', Migration::class); + $handler->down('test-down-migrations'); } +} - /** @test */ - public function testHandlerCallsDownOnMigration() - { - $this->migration->expects($this->once())->method('down'); +class Logger extends BaseLogger +{ + public function add($plugin_key, MigrationContract $migration, $batch) { } + public function remove($plugin_key, MigrationContract $migration) { } - $handler = new Handler($this->worker, $this->downLogger); - $handler->add('test', $this->migration); - $handler->down('test'); + public function getLoggedMigrations($plugin_keys) + { + if (in_array('test-down-migrations', $plugin_keys)) { + return ['test-migration']; + } else { + return []; + } } - /** @test */ - public function testHandlerDoesntCallDifferentPluginMigrations() - { - $this->migration->expects($this->never())->method($this->anything()); + public function getHighestBatchNumber() { } +} - $handler = new Handler($this->worker, $this->logger); - $handler->add('another-key', $this->migration); - $handler->up('test'); - } +class Worker extends BaseWorker +{ + public function getPrefix() { } + public function getDatabaseName() { } + public function query($query) { } + public function getResults($query) { } +} - /** @test */ - public function testHandlerDoesntAcceptMigrationsWithReservedName() +class Migration extends BaseMigration +{ + public static function id() { - $this->expectException(ReservedNameException::class); - - $handler = new Handler($this->worker, $this->logger); - $handler->add('core', $this->migration); + return 'test-migration'; } - /** @test */ - public function testHandlerDoesntUpMigrationsWithReservedName() + /** + * @throws \Exception + */ + public function up() { - $this->expectException(ReservedNameException::class); - - $handler = new Handler($this->worker, $this->logger); - $handler->up('core', $this->migration); + throw new \Exception('up method called'); } - /** @test */ - public function testHandlerDoesntDownMigrationsWithReservedName() + /** + * @throws \Exception + */ + public function down() { - $this->expectException(ReservedNameException::class); - - $handler = new Handler($this->worker, $this->logger); - $handler->down('core', $this->migration); + throw new \Exception('down method called'); } -} +} \ No newline at end of file