From 10bdfcb17f40b542028d53875d3947db7aae65bf Mon Sep 17 00:00:00 2001 From: "Jonathan.Wage" Date: Wed, 19 Sep 2007 19:33:00 +0000 Subject: [PATCH] Changes for new migration code. --- lib/Doctrine.php | 17 ++++- lib/Doctrine/Export.php | 10 ++- lib/Doctrine/Export/Firebird.php | 2 +- lib/Doctrine/Export/Frontbase.php | 2 +- lib/Doctrine/Export/Mssql.php | 2 +- lib/Doctrine/Export/Mysql.php | 5 +- lib/Doctrine/Export/Oracle.php | 2 +- lib/Doctrine/Export/Pgsql.php | 2 +- lib/Doctrine/Export/Sqlite.php | 94 ++++++++++++++++++++++++- lib/Doctrine/Migration.php | 107 +++++++++++++++++++++++++---- lib/Doctrine/Migration/Process.php | 4 +- playground/index.php | 5 +- tests/MigrationTestCase.php | 60 ++++++++++++++++ 13 files changed, 278 insertions(+), 34 deletions(-) diff --git a/lib/Doctrine.php b/lib/Doctrine.php index 515f679aa..3169e5f72 100644 --- a/lib/Doctrine.php +++ b/lib/Doctrine.php @@ -465,6 +465,8 @@ final class Doctrine /** * getLoadedModels * + * Get all the loaded models, you can provide an array of classes or it will use get_declared_classes() + * * @package default * @author Jonathan H. Wage */ @@ -507,12 +509,21 @@ final class Doctrine return $loadedModels; } - public function getConnectionByTableName($tableName) + /** + * getConnectionByTableName + * + * Get the connection object for a table by the actual table name + * + * @param string $tableName + * @return void + * @author Jonathan H. Wage + */ + public static function getConnectionByTableName($tableName) { - $loadedModels = Doctrine::getLoadedModels(); + $loadedModels = self::getLoadedModels(); foreach ($loadedModels AS $name) { - $model = new $model(); + $model = new $name(); $table = $model->getTable(); if ($table->getTableName() == $tableName) { diff --git a/lib/Doctrine/Export.php b/lib/Doctrine/Export.php index 0fcc5a698..92e6f3658 100644 --- a/lib/Doctrine/Export.php +++ b/lib/Doctrine/Export.php @@ -547,9 +547,13 @@ class Doctrine_Export extends Doctrine_Connection_Module * actually perform them otherwise. * @return void */ - public function alterTable($name, array $changes, $check) + public function alterTable($name, array $changes, $check = false) { - $this->conn->execute($this->alterTableSql($name, $changes, $check)); + $sql = $this->alterTableSql($name, $changes, $check); + + if (is_string($sql) && $sql) { + $this->conn->execute($sql); + } } /** * generates the sql for altering an existing table @@ -563,7 +567,7 @@ class Doctrine_Export extends Doctrine_Connection_Module * @see Doctrine_Export::alterTable() * @return string */ - public function alterTableSql($name, array $changes, $check) + public function alterTableSql($name, array $changes, $check = false) { throw new Doctrine_Export_Exception('Alter table not supported by this driver.'); } diff --git a/lib/Doctrine/Export/Firebird.php b/lib/Doctrine/Export/Firebird.php index d69f5d602..e1d0d8269 100644 --- a/lib/Doctrine/Export/Firebird.php +++ b/lib/Doctrine/Export/Firebird.php @@ -300,7 +300,7 @@ class Doctrine_Export_Firebird extends Doctrine_Export * actually perform them otherwise. * @return void */ - public function alterTable($name, array $changes, $check) + public function alterTable($name, array $changes, $check = false) { foreach ($changes as $changeName => $change) { switch ($changeName) { diff --git a/lib/Doctrine/Export/Frontbase.php b/lib/Doctrine/Export/Frontbase.php index 1c54b7bc9..17b76a1ed 100644 --- a/lib/Doctrine/Export/Frontbase.php +++ b/lib/Doctrine/Export/Frontbase.php @@ -157,7 +157,7 @@ class Doctrine_Export_Frontbase extends Doctrine_Export * * @return boolean */ - public function alterTable($name, array $changes, $check) + public function alterTable($name, array $changes, $check = false) { foreach ($changes as $changeName => $change) { switch ($changeName) { diff --git a/lib/Doctrine/Export/Mssql.php b/lib/Doctrine/Export/Mssql.php index 40ef6a9ec..475c9fc32 100644 --- a/lib/Doctrine/Export/Mssql.php +++ b/lib/Doctrine/Export/Mssql.php @@ -161,7 +161,7 @@ class Doctrine_Export_Mssql extends Doctrine_Export * actually perform them otherwise. * @return void */ - public function alterTable($name, array $changes, $check) + public function alterTable($name, array $changes, $check = false) { foreach ($changes as $changeName => $change) { switch ($changeName) { diff --git a/lib/Doctrine/Export/Mysql.php b/lib/Doctrine/Export/Mysql.php index bd2d84f4d..5b0672728 100644 --- a/lib/Doctrine/Export/Mysql.php +++ b/lib/Doctrine/Export/Mysql.php @@ -267,7 +267,7 @@ class Doctrine_Export_Mysql extends Doctrine_Export * actually perform them otherwise. * @return boolean */ - public function alterTableSql($name, array $changes, $check) + public function alterTableSql($name, array $changes, $check = false) { if ( ! $name) { throw new Doctrine_Export_Exception('no valid table name specified'); @@ -355,7 +355,8 @@ class Doctrine_Export_Mysql extends Doctrine_Export } $name = $this->conn->quoteIdentifier($name, true); - return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); + + return 'ALTER TABLE ' . $name . ' ' . $query; } /** * create sequence diff --git a/lib/Doctrine/Export/Oracle.php b/lib/Doctrine/Export/Oracle.php index 673fea461..b6c8df140 100644 --- a/lib/Doctrine/Export/Oracle.php +++ b/lib/Doctrine/Export/Oracle.php @@ -398,7 +398,7 @@ END; * actually perform them otherwise. * @return void */ - public function alterTable($name, array $changes, $check) + public function alterTable($name, array $changes, $check = false) { foreach ($changes as $changeName => $change) { diff --git a/lib/Doctrine/Export/Pgsql.php b/lib/Doctrine/Export/Pgsql.php index 29d332e7d..a6a59efba 100644 --- a/lib/Doctrine/Export/Pgsql.php +++ b/lib/Doctrine/Export/Pgsql.php @@ -182,7 +182,7 @@ class Doctrine_Export_Pgsql extends Doctrine_Export * @throws Doctrine_Connection_Exception * @return boolean */ - public function alterTable($name, array $changes, $check) + public function alterTable($name, array $changes, $check = false) { foreach ($changes as $changeName => $change) { switch ($changeName) { diff --git a/lib/Doctrine/Export/Sqlite.php b/lib/Doctrine/Export/Sqlite.php index e67bafba8..75033f980 100644 --- a/lib/Doctrine/Export/Sqlite.php +++ b/lib/Doctrine/Export/Sqlite.php @@ -325,4 +325,96 @@ class Doctrine_Export_Sqlite extends Doctrine_Export return 'DROP TABLE ' . $sequenceName; } -} + + public function alterTableSql($name, array $changes, $check = false) + { + if ( ! $name) { + throw new Doctrine_Export_Exception('no valid table name specified'); + } + foreach ($changes as $changeName => $change) { + switch ($changeName) { + case 'add': + case 'remove': + case 'change': + case 'rename': + case 'name': + break; + default: + throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported'); + } + } + + if ($check) { + return true; + } + + $query = ''; + if ( ! empty($changes['name'])) { + $change_name = $this->conn->quoteIdentifier($changes['name']); + $query .= 'RENAME TO ' . $change_name; + } + + if ( ! empty($changes['add']) && is_array($changes['add'])) { + foreach ($changes['add'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + $query.= 'ADD ' . $this->getDeclaration($field['type'], $fieldName, $field); + } + } + + if ( ! empty($changes['remove']) && is_array($changes['remove'])) { + foreach ($changes['remove'] as $fieldName => $field) { + if ($query) { + $query .= ', '; + } + $fieldName = $this->conn->quoteIdentifier($fieldName); + $query .= 'DROP ' . $fieldName; + } + } + + $rename = array(); + if ( ! empty($changes['rename']) && is_array($changes['rename'])) { + foreach ($changes['rename'] as $fieldName => $field) { + $rename[$field['name']] = $fieldName; + } + } + + if ( ! empty($changes['change']) && is_array($changes['change'])) { + foreach ($changes['change'] as $fieldName => $field) { + if ($query) { + $query.= ', '; + } + if (isset($rename[$fieldName])) { + $oldFieldName = $rename[$fieldName]; + unset($rename[$fieldName]); + } else { + $oldFieldName = $fieldName; + } + $oldFieldName = $this->conn->quoteIdentifier($oldFieldName, true); + $query .= 'CHANGE ' . $oldFieldName . ' ' + . $this->getDeclaration($field['definition']['type'], $fieldName, $field['definition']); + } + } + + if ( ! empty($rename) && is_array($rename)) { + foreach ($rename as $renameName => $renamedField) { + if ($query) { + $query.= ', '; + } + $field = $changes['rename'][$renamedField]; + $renamedField = $this->conn->quoteIdentifier($renamedField, true); + $query .= 'CHANGE ' . $renamedField . ' ' + . $this->getDeclaration($field['definition']['type'], $field['name'], $field['definition']); + } + } + + if ( ! $query) { + return false; + } + + $name = $this->conn->quoteIdentifier($name, true); + + return 'ALTER TABLE ' . $name . ' ' . $query; + } +} \ No newline at end of file diff --git a/lib/Doctrine/Migration.php b/lib/Doctrine/Migration.php index 772be52d5..0360a15cd 100644 --- a/lib/Doctrine/Migration.php +++ b/lib/Doctrine/Migration.php @@ -43,7 +43,35 @@ class Doctrine_Migration 'added_indexes' => array(), 'removed_indexes' => array()); - static public function migration($directory, $from, $to) + static public function setCurrentVersion($number) + { + $conn = Doctrine_Manager::connection(); + + try { + $conn->export->createTable('migration_version', array('version' => array('type' => 'integer', 'size' => 11))); + } catch(Exception $e) { + + } + + $current = self::getCurrentVersion(); + + if (!$current) { + $conn->exec("INSERT INTO migration_version (version) VALUES ($number)"); + } else { + $conn->exec("UPDATE migration_version SET version = $number"); + } + } + + static public function getCurrentVersion() + { + $conn = Doctrine_Manager::connection(); + + $result = $conn->fetchColumn("SELECT version FROM migration_version"); + + return isset($result[0]) ? $result[0]:false; + } + + static public function migration($from, $to) { if ($from === $to || $from === 0) { throw new Doctrine_Migration_Exception('You specified an invalid migration path. The from and to cannot be the same and from cannot be zero.'); @@ -53,29 +81,80 @@ class Doctrine_Migration if ($direction === 'up') { for ($i = $from + 1; $i <= $to; $i++) { - self::doDirectionStep($directory, $direction, $i); + self::doDirectionStep($direction, $i); } } else { for ($i = $from; $i > $to; $i--) { - self::doDirectionStep($directory, $direction, $i); + self::doDirectionStep($direction, $i); } } + + self::setCurrentVersion($to); + } + + public static function doDirectionStep($direction, $num) + { + $className = 'Migration' . $num; + + if (class_exists($className)) { + $migrate = new $className(); + $migrate->migrate($direction); + } else { + throw new Doctrine_Migration_Exception('Could not find migration class: ' . $className); + } } - public static function doDirectionStep($directory, $direction, $num) + public static function loadMigrationClasses($directory) { - $className = 'Migration' . $num; - $fileName = $className . '.class.php'; - $filePath = $directory . DIRECTORY_SEPARATOR . $fileName; - - if (file_exists($filePath)) { - require_once($filePath); - - if (class_exists($className)) { - $migrate = new $className(); - $migrate->migrate($direction); + $classes = get_declared_classes(); + + if ($directory !== null) { + foreach ((array) $directory as $dir) { + $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), + RecursiveIteratorIterator::LEAVES_ONLY); + + foreach ($it as $file) { + $e = explode('.', $file->getFileName()); + if (end($e) === 'php' && strpos($file->getFileName(), '.inc') === false) { + require_once $file->getPathName(); + } + } } + + $classes = array_diff(get_declared_classes(), $classes); } + + return self::getLoadedMigrationClasses($classes); + } + + public static function getLoadedMigrationClasses($classes = null) + { + if ($classes === null) { + $classes = get_declared_classes(); + } + + $parent = new ReflectionClass('Doctrine_Migration'); + $loadedClasses = array(); + + foreach ($classes as $name) { + $class = new ReflectionClass($name); + + while ($class->isSubclassOf($parent)) { + + $class = $class->getParentClass(); + if ($class === false) { + break; + } + } + + if ($class === false) { + continue; + } + + $loadedClasses[] = $name; + } + + return $loadedClasses; } public function migrate($direction) diff --git a/lib/Doctrine/Migration/Process.php b/lib/Doctrine/Migration/Process.php index e64ad39fe..b87f87304 100644 --- a/lib/Doctrine/Migration/Process.php +++ b/lib/Doctrine/Migration/Process.php @@ -37,7 +37,7 @@ class Doctrine_Migration_Process } public function processCreatedTables($tables) - { + { foreach ($tables as $table) { $conn = $this->getConnection($table['tableName']); @@ -94,7 +94,7 @@ class Doctrine_Migration_Process $options = $column['options']; $options['type'] = $column['type']; - $conn->export->alterTable($column['tableName'], array('change' => array($column['oldColumnName'] => array('definition' => $options))), true); + $conn->export->alterTable($column['tableName'], array('change' => array($column['columnName'] => array('definition' => $options))), true); } } diff --git a/playground/index.php b/playground/index.php index c8cfd2815..fc4aec852 100644 --- a/playground/index.php +++ b/playground/index.php @@ -1,7 +1,4 @@ assertEqual(Doctrine_Migration::getCurrentVersion(), 1); + } } + +class MigrationTestTable extends Doctrine_Record +{ + public function setTableDefinition() + { + $this->hasColumn('field1', 'string'); + } +} + +class Migration2 extends Doctrine_Migration +{ + public function up() + { + $this->createTable('migration_test_table', array('field1' => array('type' => 'string'))); + } + + public function down() + { + $this->dropTable('migration_test_table'); + } +} + +class Migration3 extends Doctrine_Migration +{ + public function up() + { + $this->addColumn('migration_test_table', 'field1', 'string'); + } + + public function down() + { + $this->renameColumn('migration_test_table', 'field1', 'field2'); + } +} + +class Migration4 extends Doctrine_Migration +{ + public function up() + { + $this->changeColumn('migration_test_table', 'field1', 'integer'); + } + + public function down() + { + $this->changeColumn('migration_test_table', 'field1', 'string'); + } +} \ No newline at end of file