diff --git a/README.md b/README.md index 2e1e135..01200c3 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,14 @@ The user will see a console which would adjust database to contain required tabl Of course it's also possible to perform migration without visual feedback: ``` php -$changes = (new \atk4\schema\Migration\MySQL(new User($app->db)))->migrate(); +$changes = (\atk4\schema\Migration::getMigration(new User($app->db)))->migrate(); ``` If you need a more fine-graned migration, you can define them in great detail. ``` php // create table -$migration = new \atk4\schema\Migration\MySQL($app->db); +$migration = \atk4\schema\Migration::getMigration($app->db); $migration->table('user') ->id() ->field('name') @@ -48,13 +48,14 @@ $migration->table('user') ->create(); // or alter -$migration = new \atk4\schema\Migration\MySQL($app->db); +$migration = \atk4\schema\Migration::getMigration($app->db); $migration->table('user') ->newField('age', ['type'=>'integer']) ->alter(); ``` -You can also use `\atk4\schema\Migration\Sqlite`. Other SQL databases are not yet supported. Field declaration uses same types as [ATK Data](https://github.com/atk4/data). +Currently we fully support MySQL and SQLite connections, partly PgSQL and Oracle connections. Other SQL databases are not yet supported. +Field declaration uses same types as [ATK Data](https://github.com/atk4/data). ## Examples @@ -63,7 +64,7 @@ queries using DSQL. ``` php table('user')->drop(); $m->field('id'); $m->field('name', ['type'=>'string']); @@ -72,7 +73,7 @@ $m->field('bio'); $m->create(); ``` -`schema\Snapshot` is a simple class that can record and restore +`schema\Snapshot` (NOT IMPLEMENTED) is a simple class that can record and restore table contents: ``` php @@ -112,7 +113,7 @@ against any other state. - Automatically add 'id' field by default - Create tables for you -- Detect types (int, string, etc) +- Detect types (int, string, date, boolean etc) - Hides ID values if you don't pass them ## Installation diff --git a/demos/modelmigrator.php b/demos/modelmigrator.php index a2dbfdc..3a2d285 100644 --- a/demos/modelmigrator.php +++ b/demos/modelmigrator.php @@ -16,7 +16,7 @@ public function init() try { // apply migrator - (new \atk4\schema\Migration\MySQL($m))->migrate(); + (\atk4\schema\Migration::getMigration($m))->migrate(); // ok, now we surely have DB! diff --git a/src/Migration.php b/src/Migration.php index 204face..adb2dcc 100644 --- a/src/Migration.php +++ b/src/Migration.php @@ -3,6 +3,9 @@ namespace atk4\schema; use atk4\core\Exception; +use atk4\Data\Model; +use atk4\data\Persistence; +use atk4\dsql\Connection; use atk4\dsql\Expression; class Migration extends Expression @@ -18,7 +21,7 @@ class Migration extends Expression 'rename' => 'rename table {old_table} to {table}', ]; - /** @var \atk4\dsql\Connection Database connection */ + /** @var Connection Database connection */ public $connection; /** @@ -65,47 +68,99 @@ class Migration extends Expression public $mapToAgile = []; /** - * Create new migration. + * Factory method to get correct Migration subclass object depending on connection given. + * + * @param Connection|Persistence|Model $source + * @param array $params * - * @param \atk4\dsql\Connection|\atk4\data\Persistence|\atk4\data\Model $source - * @param array $params + * @return Migration Subclass */ - public function __construct($source, $params = []) + public static function getMigration($source, $params = []) { - parent::__construct($params); - - if ($source instanceof \atk4\dsql\Connection) { - $this->connection = $source; - - return; - } elseif ($source instanceof \atk4\data\Persistence\SQL) { - $this->connection = $source->connection; - - return; - } elseif ($source instanceof \atk4\data\Model) { - if ($source->persistence && ($source->persistence instanceof \atk4\data\Persistence\SQL)) { - $this->connection = $source->persistence->connection; - - $this->setModel($source); + $c = static::getConnection($source); + + switch ($c->driver) { + case 'sqlite': + return new Migration\SQLite($source, $params); + case 'mysql': + return new Migration\MySQL($source, $params); + case 'pgsql': + return new Migration\PgSQL($source, $params); + case 'oci': + return new Migration\Oracle($source, $params); + default: + throw new Exception([ + 'Not sure which migration class to use for your DSN', + 'driver' => $c->driver, + 'source' => $source, + ]); + } + } - return; - } + /** + * Static method to extract DB driver from Connection, Persistence or Model. + * + * @param Connection|Persistence|Model $source + */ + public static function getConnection($source) + { + if ($source instanceof Connection) { + return $source; + } elseif ($source instanceof Persistence\SQL) { + return $source->connection; + } elseif ( + $source instanceof Model + && $source->persistence + && ($source->persistence instanceof Persistence\SQL) + ) { + return $source->persistence->connection; } - throw new \atk4\core\Exception([ + throw new Exception([ 'Source is specified incorrectly. Must be Connection, Persistence or initialized Model', 'source' => $source, ]); } + /** + * Create new migration. + * + * @param Connection|Persistence|Model $source + * @param array $params + */ + public function __construct($source, $params = []) + { + parent::__construct($params); + + $this->setSource($source); + } + + /** + * Sets source of migration. + * + * @param Connection|Persistence|Model $source + */ + public function setSource($source) + { + $this->connection = static::getConnection($source); + + if ( + $source instanceof Model + && $source->persistence + && ($source->persistence instanceof Persistence\SQL) + ) { + $this->setModel($source); + } + } + /** * Sets model. * - * @param \atk4\data\Model $m + * @param Model $m * - * @return \atk4\data\Model + * @return Model */ - public function setModel(\atk4\data\Model $m) + public function setModel(Model $m) { $this->table($m->table); @@ -308,14 +363,14 @@ public function _render_statements() * Create rough model from current set of $this->args['fields']. This is not * ideal solution but is designed as a drop-in solution. * - * @param \atk4\data\Persistence $persistence - * @param string $table + * @param Persistence $persistence + * @param string $table * - * @return \atk4\data\Model + * @return Model */ public function createModel($persistence, $table = null) { - $m = new \atk4\data\Model([$persistence, 'table'=>$table ?: $this['table'] = $table]); + $m = new Model([$persistence, 'table'=>$table ?: $this['table'] = $table]); foreach ($this->_getFields() as $field => $options) { if ($field == 'id') { @@ -385,7 +440,7 @@ public function dropField($field) * Return database table descriptions. * DB engine specific. * - * @todo Convert to abstract function + * @todo Maybe convert to abstract function * * @param string $table * diff --git a/src/PHPUnit_SchemaTestCase.php b/src/PHPUnit_SchemaTestCase.php index a49817d..8f6df58 100644 --- a/src/PHPUnit_SchemaTestCase.php +++ b/src/PHPUnit_SchemaTestCase.php @@ -2,7 +2,9 @@ namespace atk4\schema; +use atk4\data\Model; use atk4\data\Persistence; +use atk4\dsql\Connection; // NOTE: This class should stay here in this namespace because other repos rely on it. For example, atk4\data tests class PHPUnit_SchemaTestCase extends \atk4\core\PHPUnit_AgileTestCase @@ -35,14 +37,7 @@ public function setUp() $pass = isset($GLOBALS['DB_PASSWD']) ? $GLOBALS['DB_PASSWD'] : null; $this->db = Persistence::connect($this->dsn, $user, $pass); - - // extract dirver - if ($this->debug) { - list($dumper_driver, $this->driver, $junk) = explode(':', $this->dsn, 3); - } else { - list($this->driver, $junk) = explode(':', $this->dsn, 2); - } - $this->driver = strtolower($this->driver); + $this->driver = $this->db->connection->driver; } public function tearDown() @@ -55,28 +50,13 @@ public function tearDown() /** * Create and return appropriate Migration object. * - * @param \atk4\dsql\Connection|\atk4\data\Persistence|\atk4\data\Model $m + * @param Connection|Persistence|Model $m * * @return Migration */ public function getMigration($m = null) { - switch ($this->driver) { - case 'sqlite': - return new \atk4\schema\Migration\SQLite($m ?: $this->db); - case 'mysql': - return new \atk4\schema\Migration\MySQL($m ?: $this->db); - case 'pgsql': - return new \atk4\schema\Migration\PgSQL($m ?: $this->db); - case 'oci': - return new \atk4\schema\Migration\Oracle($m ?: $this->db); - default: - throw new \atk4\core\Exception([ - 'Not sure which migration class to use for your DSN', - 'driver' => $this->driver, - 'dsn' => $this->dsn, - ]); - } + return \atk4\schema\Migration::getMigration($m ?: $this->db); } /** @@ -87,7 +67,7 @@ public function getMigration($m = null) */ public function dropTable($table) { - $this->db->connection->expr('drop table if exists {}', [$table])->execute(); + $this->getMigration()->table($table)->drop(); } /**