parent
185ca46d42
commit
bfc536942e
@ -1 +0,0 @@ |
||||
Subproject commit 6c527d8ccbccec5257bb2c92bca973653b1e3976 |
||||
@ -0,0 +1 @@ |
||||
src_dir: lib |
||||
@ -0,0 +1,108 @@ |
||||
# Doctrine Database Migrations |
||||
|
||||
## Status |
||||
|
||||
[](https://travis-ci.org/doctrine/migrations) |
||||
[](https://www.versioneye.com/php/doctrine:migrations/) |
||||
[](https://scrutinizer-ci.com/g/doctrine/migrations/?branch=master) |
||||
[](https://scrutinizer-ci.com/g/doctrine/migrations/?branch=master) |
||||
|
||||
|
||||
## Official Documentation |
||||
|
||||
All available documentation can be found [here](http://docs.doctrine-project.org/projects/doctrine-migrations/en/latest/). |
||||
|
||||
The repository containing the documentation is [there](https://github.com/doctrine/migrations-documentation). |
||||
|
||||
## Working with Doctrine Migrations |
||||
|
||||
### Using the integration of your framework |
||||
|
||||
* symfony 2 [doctrine/doctrine-migrations-bundle](https://packagist.org/packages/doctrine/doctrine-migrations-bundle) |
||||
* ZF2 [doctrine/doctrine-orm-module](https://packagist.org/packages/doctrine/doctrine-orm-module) |
||||
* laravel [mitchellvanw/laravel-doctrine](https://packagist.org/packages/mitchellvanw/laravel-doctrine) |
||||
* Silex [kurl/silex-doctrine-migrations-provider](https://packagist.org/packages/kurl/silex-doctrine-migrations-provider) |
||||
* nette [zenify/doctrine-migrations](https://packagist.org/packages/zenify/doctrine-migrations) |
||||
* others... |
||||
|
||||
### Using composer |
||||
|
||||
```composer require doctrine/migrations``` |
||||
|
||||
### Downloading the latest phar release |
||||
|
||||
You can download the [doctrine migrations phar](https://github.com/doctrine/migrations/releases) directly on the release page |
||||
|
||||
### Building Your own Phar |
||||
|
||||
Make sure Composer and all necessary dependencies are installed: |
||||
|
||||
```bash |
||||
curl -s https://getcomposer.org/installer | php |
||||
php composer.phar install --dev |
||||
``` |
||||
|
||||
Make sure that the Box project is installed: |
||||
|
||||
```bash |
||||
curl -s http://box-project.org/installer.php | php |
||||
``` |
||||
|
||||
Build the PHAR archive: |
||||
|
||||
```bash |
||||
php box.phar build |
||||
``` |
||||
|
||||
The `doctrine-migrations.phar` archive is built in the `build` directory. |
||||
|
||||
#### Creating archive disabled by INI setting |
||||
|
||||
If you receive an error that looks like: |
||||
|
||||
creating archive "build/doctrine-migrations.phar" disabled by INI setting |
||||
|
||||
This can be fixed by setting the following in your php.ini: |
||||
|
||||
```ini |
||||
; http://php.net/phar.readonly |
||||
phar.readonly = Off |
||||
``` |
||||
|
||||
## Installing Dependencies |
||||
|
||||
To install dependencies run a composer update: |
||||
|
||||
```composer update``` |
||||
|
||||
## symfony 2.3 users |
||||
|
||||
Doctrine migration need the doctrine/orm 2.4, you need to [update your composer.json](https://github.com/symfony/symfony-standard/blob/v2.3.28/composer.json#L12) to the last version of it for symfony 2.3. |
||||
|
||||
That version is compatible with the doctrine/orm 2.4 and there are [very little upgrade needed](https://github.com/doctrine/doctrine2/blob/master/UPGRADE.md#upgrade-to-24). |
||||
|
||||
## Running the unit tests |
||||
|
||||
To run the tests, you need the sqlite extension for php. |
||||
On Unix-like systems, install: |
||||
- php5-sqlite |
||||
|
||||
On Windows, enable the extension by uncommenting the following lines in php.ini |
||||
``` |
||||
extension = php_pdo_sqlite.dll |
||||
extension = php_sqlite3.dll |
||||
extension_dir = ext |
||||
``` |
||||
|
||||
Running the tests from the project root: |
||||
``` |
||||
./vendor/bin/phpunit |
||||
``` |
||||
|
||||
On Windows run phpunit from the full path |
||||
``` |
||||
php vendor/phpunit/phpunit/phpunit |
||||
``` |
||||
This appears to be some bug. |
||||
|
||||
Happy testing :-) |
||||
@ -0,0 +1,39 @@ |
||||
UPGRADE FROM 1.0-alpha1 to 1.0.0-alpha3 |
||||
======================================= |
||||
|
||||
## AbstractMigration |
||||
|
||||
### Before: |
||||
|
||||
The method getName() was defined and it's implementation would change the order in which the migration would be processed. |
||||
It would cause discrepencies between the file order in a file browser and the order of execution of the migrations. |
||||
|
||||
### After: |
||||
|
||||
The getName method as been removed | set final and new getDescription method has been added. |
||||
The goal of this method is to be able to provide context for the migration. |
||||
This context is shown for the last migrated migration when the status command is called. |
||||
|
||||
## --write-sql option from the migrate command |
||||
|
||||
### Before: |
||||
|
||||
The --write-sql option would only output sql contained in the migration and would not update the table containing the migrated migrations. |
||||
|
||||
### After: |
||||
|
||||
That option now also output the sql queries necessary to update the table containing the state of the migrations. |
||||
If you want to go back to the previous behavior just make a request on the bug tracker as for now the need for it is not very clear. |
||||
|
||||
## MigrationsVersion::VERSION |
||||
|
||||
### Before: |
||||
|
||||
MigrationsVersion::VERSION used to be a property. |
||||
The returned value was fanciful. |
||||
|
||||
### After: |
||||
|
||||
It is now a a function so that a different value can be automatically send back if it's a modified version that's used. |
||||
The returned value is now the git tag. |
||||
The tag is in lowercase as the other doctrine projects. |
||||
@ -0,0 +1,3 @@ |
||||
#!/usr/bin/env php |
||||
<?php |
||||
include('doctrine-migrations.php'); |
||||
@ -0,0 +1,93 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
$autoloadFiles = array( |
||||
__DIR__ . '/../vendor/autoload.php', |
||||
__DIR__ . '/../../../autoload.php' |
||||
); |
||||
|
||||
$autoloader = false; |
||||
foreach ($autoloadFiles as $autoloadFile) { |
||||
if (file_exists($autoloadFile)) { |
||||
require_once $autoloadFile; |
||||
$autoloader = true; |
||||
} |
||||
} |
||||
|
||||
if (!$autoloader) { |
||||
if (extension_loaded('phar') && ($uri = Phar::running())) { |
||||
echo 'The phar has been builded without the depedencies' . PHP_EOL; |
||||
} |
||||
die('vendor/autoload.php could not be found. Did you run `php composer.phar install`?'); |
||||
} |
||||
|
||||
// Support for using the Doctrine ORM convention of providing a `cli-config.php` file. |
||||
$configFile = getcwd() . DIRECTORY_SEPARATOR . 'cli-config.php'; |
||||
|
||||
$helperSet = null; |
||||
if (file_exists($configFile)) { |
||||
if ( ! is_readable($configFile)) { |
||||
trigger_error( |
||||
'Configuration file [' . $configFile . '] does not have read permission.', E_ERROR |
||||
); |
||||
} |
||||
|
||||
require $configFile; |
||||
|
||||
foreach ($GLOBALS as $helperSetCandidate) { |
||||
if ($helperSetCandidate instanceof \Symfony\Component\Console\Helper\HelperSet) { |
||||
$helperSet = $helperSetCandidate; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
$helperSet = ($helperSet) ?: new \Symfony\Component\Console\Helper\HelperSet(); |
||||
|
||||
if(class_exists('\Symfony\Component\Console\Helper\QuestionHelper')) { |
||||
$helperSet->set(new \Symfony\Component\Console\Helper\QuestionHelper(), 'question'); |
||||
} else { |
||||
$helperSet->set(new \Symfony\Component\Console\Helper\DialogHelper(), 'dialog'); |
||||
} |
||||
|
||||
|
||||
$cli = new \Symfony\Component\Console\Application('Doctrine Migrations', \Doctrine\DBAL\Migrations\MigrationsVersion::VERSION()); |
||||
$cli->setCatchExceptions(true); |
||||
$cli->setHelperSet($helperSet); |
||||
$cli->addCommands(array( |
||||
// Migrations Commands |
||||
new \Doctrine\DBAL\Migrations\Tools\Console\Command\ExecuteCommand(), |
||||
new \Doctrine\DBAL\Migrations\Tools\Console\Command\GenerateCommand(), |
||||
new \Doctrine\DBAL\Migrations\Tools\Console\Command\LatestCommand(), |
||||
new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand(), |
||||
new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand(), |
||||
new \Doctrine\DBAL\Migrations\Tools\Console\Command\VersionCommand() |
||||
)); |
||||
if ($helperSet->has('em')) { |
||||
$cli->add(new \Doctrine\DBAL\Migrations\Tools\Console\Command\DiffCommand()); |
||||
} |
||||
|
||||
$input = file_exists('migrations-input.php') |
||||
? include 'migrations-input.php' : null; |
||||
|
||||
$output = file_exists('migrations-output.php') |
||||
? include 'migrations-output.php' : null; |
||||
|
||||
$cli->run($input, $output); |
||||
|
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
class AbortMigrationException extends MigrationException |
||||
{ |
||||
} |
||||
@ -0,0 +1,189 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
use Doctrine\DBAL\Schema\Schema; |
||||
|
||||
/** |
||||
* Abstract class for individual migrations to extend from. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
abstract class AbstractMigration |
||||
{ |
||||
/** |
||||
* Reference to the Version instance representing this migration |
||||
* |
||||
* @var Version |
||||
*/ |
||||
protected $version; |
||||
|
||||
/** |
||||
* The Doctrine\DBAL\Connection instance we are migrating |
||||
* |
||||
* @var \Doctrine\DBAL\Connection |
||||
*/ |
||||
protected $connection; |
||||
|
||||
/** |
||||
* Reference to the SchemaManager instance referenced by $_connection |
||||
* |
||||
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager |
||||
*/ |
||||
protected $sm; |
||||
|
||||
/** |
||||
* Reference to the DatabasePlatform instance referenced by $_connection |
||||
* |
||||
* @var \Doctrine\DBAL\Platforms\AbstractPlatform |
||||
*/ |
||||
protected $platform; |
||||
|
||||
/** |
||||
* The OutputWriter object instance used for outputting information |
||||
* |
||||
* @var OutputWriter |
||||
*/ |
||||
private $outputWriter; |
||||
|
||||
public function __construct(Version $version) |
||||
{ |
||||
$config = $version->getConfiguration(); |
||||
|
||||
$this->version = $version; |
||||
$this->connection = $config->getConnection(); |
||||
$this->sm = $this->connection->getSchemaManager(); |
||||
$this->platform = $this->connection->getDatabasePlatform(); |
||||
$this->outputWriter = $config->getOutputWriter(); |
||||
} |
||||
|
||||
/** |
||||
* Indicates the transactional mode of this migration. |
||||
* If this function returns true (default) the migration will be executed in one transaction, |
||||
* otherwise non-transactional state will be used to execute each of the migration SQLs. |
||||
* |
||||
* Extending class should override this function to alter the return value |
||||
* |
||||
* @return bool TRUE by default. |
||||
*/ |
||||
public function isTransactional() |
||||
{ |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Get migration description |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getDescription() |
||||
{ |
||||
return ''; |
||||
} |
||||
|
||||
/** |
||||
* Print a warning message if the condition evaluates to TRUE. |
||||
* |
||||
* @param boolean $condition |
||||
* @param string $message |
||||
*/ |
||||
public function warnIf($condition, $message = '') |
||||
{ |
||||
if ($condition) { |
||||
$message = $message?: 'Unknown Reason'; |
||||
$this->outputWriter->write(sprintf( |
||||
' <warning>Warning during %s: %s</warning>', |
||||
$this->version->getExecutionState(), |
||||
$message |
||||
)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Abort the migration if the condition evaluates to TRUE. |
||||
* |
||||
* @param boolean $condition |
||||
* @param string $message |
||||
* |
||||
* @throws AbortMigrationException |
||||
*/ |
||||
public function abortIf($condition, $message = '') |
||||
{ |
||||
if ($condition) { |
||||
throw new AbortMigrationException($message ?: 'Unknown Reason'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Skip this migration (but not the next ones) if condition evaluates to TRUE. |
||||
* |
||||
* @param boolean $condition |
||||
* @param string $message |
||||
* |
||||
* @throws SkipMigrationException |
||||
*/ |
||||
public function skipIf($condition, $message = '') |
||||
{ |
||||
if ($condition) { |
||||
throw new SkipMigrationException($message ?: 'Unknown Reason'); |
||||
} |
||||
} |
||||
|
||||
public function preUp(Schema $schema) |
||||
{ |
||||
} |
||||
|
||||
public function postUp(Schema $schema) |
||||
{ |
||||
} |
||||
|
||||
public function preDown(Schema $schema) |
||||
{ |
||||
} |
||||
|
||||
public function postDown(Schema $schema) |
||||
{ |
||||
} |
||||
|
||||
abstract public function up(Schema $schema); |
||||
abstract public function down(Schema $schema); |
||||
|
||||
protected function addSql($sql, array $params = array(), array $types = array()) |
||||
{ |
||||
$this->version->addSql($sql, $params, $types); |
||||
} |
||||
|
||||
protected function write($message) |
||||
{ |
||||
$this->outputWriter->write($message); |
||||
} |
||||
|
||||
protected function throwIrreversibleMigrationException($message = null) |
||||
{ |
||||
if (null === $message) { |
||||
$message = 'This migration is irreversible and cannot be reverted.'; |
||||
} |
||||
|
||||
throw new IrreversibleMigrationException($message); |
||||
} |
||||
} |
||||
@ -0,0 +1,94 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Configuration; |
||||
|
||||
use Doctrine\DBAL\Migrations\MigrationException; |
||||
|
||||
/** |
||||
* Abstract Migration Configuration class for loading configuration information |
||||
* from a configuration file (xml or yml). |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
abstract class AbstractFileConfiguration extends Configuration |
||||
{ |
||||
/** |
||||
* The configuration file used to load configuration information |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $file; |
||||
|
||||
/** |
||||
* Whether or not the configuration file has been loaded yet or not |
||||
* |
||||
* @var boolean |
||||
*/ |
||||
private $loaded = false; |
||||
|
||||
/** |
||||
* Load the information from the passed configuration file |
||||
* |
||||
* @param string $file The path to the configuration file |
||||
* |
||||
* @throws MigrationException Throws exception if configuration file was already loaded |
||||
*/ |
||||
public function load($file) |
||||
{ |
||||
if ($this->loaded) { |
||||
throw MigrationException::configurationFileAlreadyLoaded(); |
||||
} |
||||
if (file_exists($path = getcwd() . '/' . $file)) { |
||||
$file = $path; |
||||
} |
||||
$this->file = $file; |
||||
$this->doLoad($file); |
||||
$this->loaded = true; |
||||
} |
||||
|
||||
protected function getDirectoryRelativeToFile($file, $input) |
||||
{ |
||||
$path = realpath(dirname($file) . '/' . $input); |
||||
if ($path !== false) { |
||||
$directory = $path; |
||||
} else { |
||||
$directory = $input; |
||||
} |
||||
|
||||
return $directory; |
||||
} |
||||
|
||||
public function getFile() |
||||
{ |
||||
return $this->file; |
||||
} |
||||
|
||||
/** |
||||
* Abstract method that each file configuration driver must implement to |
||||
* load the given configuration file whether it be xml, yaml, etc. or something |
||||
* else. |
||||
* |
||||
* @param string $file The path to a configuration file. |
||||
*/ |
||||
abstract protected function doLoad($file); |
||||
} |
||||
@ -0,0 +1,695 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Configuration; |
||||
|
||||
use Doctrine\DBAL\Connection; |
||||
use Doctrine\DBAL\Migrations\MigrationException; |
||||
use Doctrine\DBAL\Migrations\OutputWriter; |
||||
use Doctrine\DBAL\Migrations\Version; |
||||
use Doctrine\DBAL\Migrations\Finder\MigrationFinderInterface; |
||||
use Doctrine\DBAL\Migrations\Finder\RecursiveRegexFinder; |
||||
use Doctrine\DBAL\Schema\Column; |
||||
use Doctrine\DBAL\Schema\Table; |
||||
use Doctrine\DBAL\Types\Type; |
||||
|
||||
/** |
||||
* Default Migration Configuration object used for configuring an instance of |
||||
* the Migration class. Set the connection, version table name, register migration |
||||
* classes/versions, etc. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class Configuration |
||||
{ |
||||
/** |
||||
* Name of this set of migrations |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $name; |
||||
|
||||
/** |
||||
* Flag for whether or not the migration table has been created |
||||
* |
||||
* @var boolean |
||||
*/ |
||||
private $migrationTableCreated = false; |
||||
|
||||
/** |
||||
* Connection instance to use for migrations |
||||
* |
||||
* @var Connection |
||||
*/ |
||||
private $connection; |
||||
|
||||
/** |
||||
* OutputWriter instance for writing output during migrations |
||||
* |
||||
* @var OutputWriter |
||||
*/ |
||||
private $outputWriter; |
||||
|
||||
/** |
||||
* The migration finder implementation -- used to load migrations from a |
||||
* directory. |
||||
* |
||||
* @var MigrationFinderInterface |
||||
*/ |
||||
private $migrationFinder; |
||||
|
||||
/** |
||||
* The migration table name to track versions in |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $migrationsTableName = 'doctrine_migration_versions'; |
||||
|
||||
/** |
||||
* The path to a directory where new migration classes will be written |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $migrationsDirectory; |
||||
|
||||
/** |
||||
* Namespace the migration classes live in |
||||
* |
||||
* @var string |
||||
*/ |
||||
private $migrationsNamespace; |
||||
|
||||
/** |
||||
* Array of the registered migrations |
||||
* |
||||
* @var Version[] |
||||
*/ |
||||
private $migrations = array(); |
||||
|
||||
/** |
||||
* Construct a migration configuration object. |
||||
* |
||||
* @param Connection $connection A Connection instance |
||||
* @param OutputWriter $outputWriter A OutputWriter instance |
||||
* @param MigrationFinderInterface $finder Migration files finder |
||||
*/ |
||||
public function __construct(Connection $connection, OutputWriter $outputWriter = null, MigrationFinderInterface $finder = null) |
||||
{ |
||||
$this->connection = $connection; |
||||
if ($outputWriter === null) { |
||||
$outputWriter = new OutputWriter(); |
||||
} |
||||
$this->outputWriter = $outputWriter; |
||||
$this->migrationFinder = $finder; |
||||
} |
||||
|
||||
/** |
||||
* Validation that this instance has all the required properties configured |
||||
* |
||||
* @throws MigrationException |
||||
*/ |
||||
public function validate() |
||||
{ |
||||
if (! $this->migrationsNamespace) { |
||||
throw MigrationException::migrationsNamespaceRequired(); |
||||
} |
||||
if (! $this->migrationsDirectory) { |
||||
throw MigrationException::migrationsDirectoryRequired(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Set the name of this set of migrations |
||||
* |
||||
* @param string $name The name of this set of migrations |
||||
*/ |
||||
public function setName($name) |
||||
{ |
||||
$this->name = $name; |
||||
} |
||||
|
||||
/** |
||||
* Returns the name of this set of migrations |
||||
* |
||||
* @return string $name The name of this set of migrations |
||||
*/ |
||||
public function getName() |
||||
{ |
||||
return $this->name; |
||||
} |
||||
|
||||
/** |
||||
* Returns the OutputWriter instance |
||||
* |
||||
* @return OutputWriter $outputWriter The OutputWriter instance |
||||
*/ |
||||
public function getOutputWriter() |
||||
{ |
||||
return $this->outputWriter; |
||||
} |
||||
|
||||
/** |
||||
* Returns a timestamp version as a formatted date |
||||
* |
||||
* @param string $version |
||||
* |
||||
* @return string The formatted version |
||||
* @deprecated |
||||
*/ |
||||
public function formatVersion($version) |
||||
{ |
||||
return $this->getDateTime($version); |
||||
} |
||||
|
||||
/** |
||||
* Returns the datetime of a migration |
||||
* |
||||
* @param $version |
||||
* @return string |
||||
*/ |
||||
public function getDateTime($version) |
||||
{ |
||||
$datetime = str_replace('Version', '', $version); |
||||
$datetime = \DateTime::createFromFormat('YmdHis', $datetime); |
||||
|
||||
if ($datetime === false){ |
||||
return ''; |
||||
} |
||||
|
||||
return $datetime->format('Y-m-d H:i:s'); |
||||
} |
||||
|
||||
/** |
||||
* Returns the Connection instance |
||||
* |
||||
* @return Connection $connection The Connection instance |
||||
*/ |
||||
public function getConnection() |
||||
{ |
||||
return $this->connection; |
||||
} |
||||
|
||||
/** |
||||
* Set the migration table name |
||||
* |
||||
* @param string $tableName The migration table name |
||||
*/ |
||||
public function setMigrationsTableName($tableName) |
||||
{ |
||||
$this->migrationsTableName = $tableName; |
||||
} |
||||
|
||||
/** |
||||
* Returns the migration table name |
||||
* |
||||
* @return string $migrationsTableName The migration table name |
||||
*/ |
||||
public function getMigrationsTableName() |
||||
{ |
||||
return $this->migrationsTableName; |
||||
} |
||||
|
||||
/** |
||||
* Set the new migrations directory where new migration classes are generated |
||||
* |
||||
* @param string $migrationsDirectory The new migrations directory |
||||
*/ |
||||
public function setMigrationsDirectory($migrationsDirectory) |
||||
{ |
||||
$this->migrationsDirectory = $migrationsDirectory; |
||||
} |
||||
|
||||
/** |
||||
* Returns the new migrations directory where new migration classes are generated |
||||
* |
||||
* @return string $migrationsDirectory The new migrations directory |
||||
*/ |
||||
public function getMigrationsDirectory() |
||||
{ |
||||
return $this->migrationsDirectory; |
||||
} |
||||
|
||||
/** |
||||
* Set the migrations namespace |
||||
* |
||||
* @param string $migrationsNamespace The migrations namespace |
||||
*/ |
||||
public function setMigrationsNamespace($migrationsNamespace) |
||||
{ |
||||
$this->migrationsNamespace = $migrationsNamespace; |
||||
} |
||||
|
||||
/** |
||||
* Returns the migrations namespace |
||||
* |
||||
* @return string $migrationsNamespace The migrations namespace |
||||
*/ |
||||
public function getMigrationsNamespace() |
||||
{ |
||||
return $this->migrationsNamespace; |
||||
} |
||||
|
||||
/** |
||||
* set the implementation of the migration finder. |
||||
* |
||||
* @param $finder The new migration finder |
||||
* @return void |
||||
*/ |
||||
public function setMigrationFinder(MigrationFinderInterface $finder) |
||||
{ |
||||
$this->migrationFinder = $finder; |
||||
} |
||||
|
||||
/** |
||||
* Register migrations from a given directory. Recursively finds all files |
||||
* with the pattern VersionYYYYMMDDHHMMSS.php as the filename and registers |
||||
* them as migrations. |
||||
* |
||||
* @param string $path The root directory to where some migration classes live. |
||||
* |
||||
* @return Version[] The array of migrations registered. |
||||
*/ |
||||
public function registerMigrationsFromDirectory($path) |
||||
{ |
||||
return $this->registerMigrations($this->findMigrations($path)); |
||||
} |
||||
|
||||
/** |
||||
* Register a single migration version to be executed by a AbstractMigration |
||||
* class. |
||||
* |
||||
* @param string $version The version of the migration in the format YYYYMMDDHHMMSS. |
||||
* @param string $class The migration class to execute for the version. |
||||
* |
||||
* @return Version |
||||
* |
||||
* @throws MigrationException |
||||
*/ |
||||
public function registerMigration($version, $class) |
||||
{ |
||||
$version = (string) $version; |
||||
$class = (string) $class; |
||||
if (isset($this->migrations[$version])) { |
||||
throw MigrationException::duplicateMigrationVersion($version, get_class($this->migrations[$version])); |
||||
} |
||||
$version = new Version($this, $version, $class); |
||||
$this->migrations[$version->getVersion()] = $version; |
||||
ksort($this->migrations); |
||||
|
||||
return $version; |
||||
} |
||||
|
||||
/** |
||||
* Register an array of migrations. Each key of the array is the version and |
||||
* the value is the migration class name. |
||||
* |
||||
* |
||||
* @param array $migrations |
||||
* |
||||
* @return Version[] |
||||
*/ |
||||
public function registerMigrations(array $migrations) |
||||
{ |
||||
$versions = array(); |
||||
foreach ($migrations as $version => $class) { |
||||
$versions[] = $this->registerMigration($version, $class); |
||||
} |
||||
|
||||
return $versions; |
||||
} |
||||
|
||||
/** |
||||
* Get the array of registered migration versions. |
||||
* |
||||
* @return Version[] $migrations |
||||
*/ |
||||
public function getMigrations() |
||||
{ |
||||
return $this->migrations; |
||||
} |
||||
|
||||
/** |
||||
* Returns the Version instance for a given version in the format YYYYMMDDHHMMSS. |
||||
* |
||||
* @param string $version The version string in the format YYYYMMDDHHMMSS. |
||||
* |
||||
* @return Version |
||||
* |
||||
* @throws MigrationException Throws exception if migration version does not exist. |
||||
*/ |
||||
public function getVersion($version) |
||||
{ |
||||
if ( ! isset($this->migrations[$version])) { |
||||
throw MigrationException::unknownMigrationVersion($version); |
||||
} |
||||
|
||||
return $this->migrations[$version]; |
||||
} |
||||
|
||||
/** |
||||
* Check if a version exists. |
||||
* |
||||
* @param string $version |
||||
* |
||||
* @return boolean |
||||
*/ |
||||
public function hasVersion($version) |
||||
{ |
||||
return isset($this->migrations[$version]); |
||||
} |
||||
|
||||
/** |
||||
* Check if a version has been migrated or not yet |
||||
* |
||||
* @param Version $version |
||||
* |
||||
* @return boolean |
||||
*/ |
||||
public function hasVersionMigrated(Version $version) |
||||
{ |
||||
$this->createMigrationTable(); |
||||
|
||||
$version = $this->connection->fetchColumn( |
||||
"SELECT version FROM " . $this->migrationsTableName . " WHERE version = ?", |
||||
array($version->getVersion()) |
||||
); |
||||
|
||||
return $version !== false; |
||||
} |
||||
|
||||
/** |
||||
* Returns all migrated versions from the versions table, in an array. |
||||
* |
||||
* @return Version[] |
||||
*/ |
||||
public function getMigratedVersions() |
||||
{ |
||||
$this->createMigrationTable(); |
||||
|
||||
$ret = $this->connection->fetchAll("SELECT version FROM " . $this->migrationsTableName); |
||||
$versions = array(); |
||||
foreach ($ret as $version) { |
||||
$versions[] = current($version); |
||||
} |
||||
|
||||
return $versions; |
||||
} |
||||
|
||||
/** |
||||
* Returns an array of available migration version numbers. |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function getAvailableVersions() |
||||
{ |
||||
$availableVersions = array(); |
||||
foreach ($this->migrations as $migration) { |
||||
$availableVersions[] = $migration->getVersion(); |
||||
} |
||||
|
||||
return $availableVersions; |
||||
} |
||||
|
||||
/** |
||||
* Returns the current migrated version from the versions table. |
||||
* |
||||
* @return string |
||||
*/ |
||||
public function getCurrentVersion() |
||||
{ |
||||
$this->createMigrationTable(); |
||||
|
||||
$where = null; |
||||
if (!empty($this->migrations)) { |
||||
$migratedVersions = array(); |
||||
foreach ($this->migrations as $migration) { |
||||
$migratedVersions[] = sprintf("'%s'", $migration->getVersion()); |
||||
} |
||||
$where = " WHERE version IN (" . implode(', ', $migratedVersions) . ")"; |
||||
} |
||||
|
||||
$sql = sprintf("SELECT version FROM %s%s ORDER BY version DESC", |
||||
$this->migrationsTableName, $where |
||||
); |
||||
|
||||
$sql = $this->connection->getDatabasePlatform()->modifyLimitQuery($sql, 1); |
||||
$result = $this->connection->fetchColumn($sql); |
||||
|
||||
return $result !== false ? (string) $result : '0'; |
||||
} |
||||
|
||||
/** |
||||
* Returns the version prior to the current version. |
||||
* |
||||
* @return string|null A version string, or null if the current version is |
||||
* the first. |
||||
*/ |
||||
public function getPrevVersion() |
||||
{ |
||||
return $this->getRelativeVersion($this->getCurrentVersion(), -1); |
||||
} |
||||
|
||||
/** |
||||
* Returns the version following the current version. |
||||
* |
||||
* @return string|null A version string, or null if the current version is |
||||
* the latest. |
||||
*/ |
||||
public function getNextVersion() |
||||
{ |
||||
return $this->getRelativeVersion($this->getCurrentVersion(), 1); |
||||
} |
||||
|
||||
/** |
||||
* Returns the version with the specified offset to the specified version. |
||||
* |
||||
* @return string|null A version string, or null if the specified version |
||||
* is unknown or the specified delta is not within the |
||||
* list of available versions. |
||||
*/ |
||||
public function getRelativeVersion($version, $delta) |
||||
{ |
||||
$versions = array_keys($this->migrations); |
||||
array_unshift($versions, 0); |
||||
$offset = array_search($version, $versions); |
||||
if ($offset === false || !isset($versions[$offset + $delta])) { |
||||
// Unknown version or delta out of bounds. |
||||
return null; |
||||
} |
||||
|
||||
return (string) $versions[$offset + $delta]; |
||||
} |
||||
|
||||
/** |
||||
* Returns the version number from an alias. |
||||
* |
||||
* Supported aliases are: |
||||
* - first: The very first version before any migrations have been run. |
||||
* - current: The current version. |
||||
* - prev: The version prior to the current version. |
||||
* - next: The version following the current version. |
||||
* - latest: The latest available version. |
||||
* |
||||
* If an existing version number is specified, it is returned verbatimly. |
||||
* |
||||
* @return string|null A version number, or null if the specified alias |
||||
* does not map to an existing version, e.g. if "next" |
||||
* is passed but the current version is already the |
||||
* latest. |
||||
*/ |
||||
public function resolveVersionAlias($alias) |
||||
{ |
||||
if ($this->hasVersion($alias)) { |
||||
return $alias; |
||||
} |
||||
switch ($alias) { |
||||
case 'first': |
||||
return '0'; |
||||
case 'current': |
||||
return $this->getCurrentVersion(); |
||||
case 'prev': |
||||
return $this->getPrevVersion(); |
||||
case 'next': |
||||
return $this->getNextVersion(); |
||||
case 'latest': |
||||
return $this->getLatestVersion(); |
||||
default: |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the total number of executed migration versions |
||||
* |
||||
* @return integer |
||||
*/ |
||||
public function getNumberOfExecutedMigrations() |
||||
{ |
||||
$this->createMigrationTable(); |
||||
|
||||
$result = $this->connection->fetchColumn("SELECT COUNT(version) FROM " . $this->migrationsTableName); |
||||
|
||||
return $result !== false ? $result : 0; |
||||
} |
||||
|
||||
/** |
||||
* Returns the total number of available migration versions |
||||
* |
||||
* @return integer |
||||
*/ |
||||
public function getNumberOfAvailableMigrations() |
||||
{ |
||||
return count($this->migrations); |
||||
} |
||||
|
||||
/** |
||||
* Returns the latest available migration version. |
||||
* |
||||
* @return string The version string in the format YYYYMMDDHHMMSS. |
||||
*/ |
||||
public function getLatestVersion() |
||||
{ |
||||
$versions = array_keys($this->migrations); |
||||
$latest = end($versions); |
||||
|
||||
return $latest !== false ? (string) $latest : '0'; |
||||
} |
||||
|
||||
/** |
||||
* Create the migration table to track migrations with. |
||||
* |
||||
* @return boolean Whether or not the table was created. |
||||
*/ |
||||
public function createMigrationTable() |
||||
{ |
||||
$this->validate(); |
||||
|
||||
if ($this->migrationTableCreated) { |
||||
return false; |
||||
} |
||||
|
||||
if ( ! $this->connection->getSchemaManager()->tablesExist(array($this->migrationsTableName))) { |
||||
$columns = array( |
||||
'version' => new Column('version', Type::getType('string'), array('length' => 255)), |
||||
); |
||||
$table = new Table($this->migrationsTableName, $columns); |
||||
$table->setPrimaryKey(array('version')); |
||||
$this->connection->getSchemaManager()->createTable($table); |
||||
|
||||
$this->migrationTableCreated = true; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
$this->migrationTableCreated = true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Returns the array of migrations to executed based on the given direction |
||||
* and target version number. |
||||
* |
||||
* @param string $direction The direction we are migrating. |
||||
* @param string $to The version to migrate to. |
||||
* |
||||
* @return Version[] $migrations The array of migrations we can execute. |
||||
*/ |
||||
public function getMigrationsToExecute($direction, $to) |
||||
{ |
||||
if ($direction === 'down') { |
||||
if (count($this->migrations)) { |
||||
$allVersions = array_reverse(array_keys($this->migrations)); |
||||
$classes = array_reverse(array_values($this->migrations)); |
||||
$allVersions = array_combine($allVersions, $classes); |
||||
} else { |
||||
$allVersions = array(); |
||||
} |
||||
} else { |
||||
$allVersions = $this->migrations; |
||||
} |
||||
$versions = array(); |
||||
$migrated = $this->getMigratedVersions(); |
||||
foreach ($allVersions as $version) { |
||||
if ($this->shouldExecuteMigration($direction, $version, $to, $migrated)) { |
||||
$versions[$version->getVersion()] = $version; |
||||
} |
||||
} |
||||
|
||||
return $versions; |
||||
} |
||||
|
||||
/** |
||||
* Find all the migrations in a given directory. |
||||
* |
||||
* @param string $path the directory to search. |
||||
* @return array |
||||
*/ |
||||
protected function findMigrations($path) |
||||
{ |
||||
return $this->getMigrationFinder()->findMigrations($path, $this->getMigrationsNamespace()); |
||||
} |
||||
|
||||
/** |
||||
* Get the migration finder, creating one if it's not present. |
||||
* |
||||
* @return MigrationFinderInterface |
||||
*/ |
||||
protected function getMigrationFinder() |
||||
{ |
||||
if (!$this->migrationFinder) { |
||||
$this->migrationFinder = new RecursiveRegexFinder(); |
||||
} |
||||
|
||||
return $this->migrationFinder; |
||||
} |
||||
|
||||
/** |
||||
* Check if we should execute a migration for a given direction and target |
||||
* migration version. |
||||
* |
||||
* @param string $direction The direction we are migrating. |
||||
* @param Version $version The Version instance to check. |
||||
* @param string $to The version we are migrating to. |
||||
* @param array $migrated Migrated versions array. |
||||
* |
||||
* @return boolean |
||||
*/ |
||||
private function shouldExecuteMigration($direction, Version $version, $to, $migrated) |
||||
{ |
||||
if ($direction === 'down') { |
||||
if ( ! in_array($version->getVersion(), $migrated)) { |
||||
return false; |
||||
} |
||||
|
||||
return $version->getVersion() > $to; |
||||
} |
||||
|
||||
if ($direction === 'up') { |
||||
if (in_array($version->getVersion(), $migrated)) { |
||||
return false; |
||||
} |
||||
|
||||
return $version->getVersion() <= $to; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,58 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Configuration; |
||||
|
||||
/** |
||||
* Load migration configuration information from a XML configuration file. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class XmlConfiguration extends AbstractFileConfiguration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
protected function doLoad($file) |
||||
{ |
||||
$xml = simplexml_load_file($file); |
||||
if (isset($xml->name)) { |
||||
$this->setName((string) $xml->name); |
||||
} |
||||
if (isset($xml->table['name'])) { |
||||
$this->setMigrationsTableName((string) $xml->table['name']); |
||||
} |
||||
if (isset($xml->{'migrations-namespace'})) { |
||||
$this->setMigrationsNamespace((string) $xml->{'migrations-namespace'}); |
||||
} |
||||
if (isset($xml->{'migrations-directory'})) { |
||||
$migrationsDirectory = $this->getDirectoryRelativeToFile($file, (string) $xml->{'migrations-directory'}); |
||||
$this->setMigrationsDirectory($migrationsDirectory); |
||||
$this->registerMigrationsFromDirectory($migrationsDirectory); |
||||
} |
||||
if (isset($xml->migrations->migration)) { |
||||
foreach ($xml->migrations->migration as $migration) { |
||||
$this->registerMigration((string) $migration['version'], (string) $migration['class']); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,61 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Configuration; |
||||
|
||||
use Symfony\Component\Yaml\Yaml; |
||||
|
||||
/** |
||||
* Load migration configuration information from a YAML configuration file. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class YamlConfiguration extends AbstractFileConfiguration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
protected function doLoad($file) |
||||
{ |
||||
$array = Yaml::parse(file_get_contents($file)); |
||||
|
||||
if (isset($array['name'])) { |
||||
$this->setName($array['name']); |
||||
} |
||||
if (isset($array['table_name'])) { |
||||
$this->setMigrationsTableName($array['table_name']); |
||||
} |
||||
if (isset($array['migrations_namespace'])) { |
||||
$this->setMigrationsNamespace($array['migrations_namespace']); |
||||
} |
||||
if (isset($array['migrations_directory'])) { |
||||
$migrationsDirectory = $this->getDirectoryRelativeToFile($file, $array['migrations_directory']); |
||||
$this->setMigrationsDirectory($migrationsDirectory); |
||||
$this->registerMigrationsFromDirectory($migrationsDirectory); |
||||
} |
||||
if (isset($array['migrations']) && is_array($array['migrations'])) { |
||||
foreach ($array['migrations'] as $migration) { |
||||
$this->registerMigration($migration['version'], $migration['class']); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,65 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Finder; |
||||
|
||||
/** |
||||
* Abstract base class for MigrationFinders |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
abstract class AbstractFinder implements MigrationFinderInterface |
||||
{ |
||||
protected static function requireOnce($path) |
||||
{ |
||||
require_once $path; |
||||
} |
||||
|
||||
protected function getRealPath($directory) |
||||
{ |
||||
$dir = realpath($directory); |
||||
if (false === $dir || !is_dir($dir)) { |
||||
throw new \InvalidArgumentException(sprintf( |
||||
'Cannot load migrations from "%s" because it is not a valid directory', |
||||
$directory |
||||
)); |
||||
} |
||||
|
||||
return $dir; |
||||
} |
||||
|
||||
/** |
||||
* Load the migrations and return an array of thoses loaded migrations |
||||
* @param $files array of migration filename found |
||||
* @param $namespace namespace of thoses migrations |
||||
* @return array constructed with the migration name as key and the value is the fully qualified name of the migration |
||||
*/ |
||||
protected function loadMigrations($files, $namespace) |
||||
{ |
||||
$migrations = []; |
||||
foreach ($files as $file) { |
||||
static::requireOnce($file); |
||||
$className = basename($file, '.php'); |
||||
$version = substr($className, 7); |
||||
$migrations[$version] = sprintf('%s\\%s', $namespace, $className); |
||||
} |
||||
|
||||
return $migrations; |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Finder; |
||||
|
||||
/** |
||||
* A MigrationFinderInterface implementation that uses `glob` and some special file and |
||||
* class names to load migrations from a directory. |
||||
* |
||||
* The migrations are expected to reside in files with the filename |
||||
* `VersionYYYYMMDDHHMMSS.php`. Each file should contain one class named |
||||
* `VersionYYYYMMDDHHMMSS`. |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
final class GlobFinder extends AbstractFinder |
||||
{ |
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function findMigrations($directory, $namespace=null) |
||||
{ |
||||
$dir = $this->getRealPath($directory); |
||||
|
||||
$files = glob(rtrim($dir, '/').'/Version*.php'); |
||||
|
||||
return $this->loadMigrations($files, $namespace); |
||||
} |
||||
} |
||||
@ -0,0 +1,40 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Finder; |
||||
|
||||
/** |
||||
* MigrationFinderInterface implementations locate migrations (classes that extend |
||||
* `Doctrine\DBAL\Migrations\AbstractMigration`) in a directory. |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
interface MigrationFinderInterface |
||||
{ |
||||
/** |
||||
* Find all the migrations in a directory for the given path and namespace. |
||||
* |
||||
* @param string $directory The directory in which to look for migrations |
||||
* @param string|null $namespace The namespace of the classes to load |
||||
* @throws InvalidArgumentException if the directory does not exist |
||||
* @return string[] An array of class names that were found with the version |
||||
* as keys. |
||||
*/ |
||||
public function findMigrations($directory, $namespace=null); |
||||
} |
||||
@ -0,0 +1,77 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Finder; |
||||
|
||||
/** |
||||
* A MigrationFinderInterface implementation that uses a RegexIterator along with a |
||||
* RecursiveDirectoryIterator. |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
final class RecursiveRegexFinder extends AbstractFinder |
||||
{ |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function findMigrations($directory, $namespace=null) |
||||
{ |
||||
$dir = $this->getRealPath($directory); |
||||
|
||||
return $this->loadMigrations($this->getMatches($this->createIterator($dir)), $namespace); |
||||
} |
||||
|
||||
/** |
||||
* Create a recursive iterator to find all the migrations in the subdirectories. |
||||
* @param $dir |
||||
* @return \RegexIterator |
||||
*/ |
||||
private function createIterator($dir) |
||||
{ |
||||
return new \RegexIterator( |
||||
new \RecursiveIteratorIterator( |
||||
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS), |
||||
\RecursiveIteratorIterator::LEAVES_ONLY |
||||
), |
||||
$this->getPattern(), |
||||
\RegexIterator::GET_MATCH |
||||
); |
||||
} |
||||
|
||||
private function getPattern() |
||||
{ |
||||
return sprintf('#^.+\\%sVersion[^\\%s]{1,255}\\.php$#i', DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR); |
||||
} |
||||
|
||||
/** |
||||
* Transform the recursiveIterator result array of array into the expected array of migration file |
||||
* @param $iteratorFilesMatch |
||||
* @return array |
||||
*/ |
||||
private function getMatches($iteratorFilesMatch) |
||||
{ |
||||
$files = []; |
||||
foreach($iteratorFilesMatch as $file) { |
||||
$files[] = $file[0]; |
||||
} |
||||
|
||||
return $files; |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
/** |
||||
* Exception to be thrown in the down() methods of migrations that signifies it |
||||
* is an irreversible migration and stops execution. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class IrreversibleMigrationException extends \Exception |
||||
{ |
||||
} |
||||
@ -0,0 +1,173 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
|
||||
/** |
||||
* Class for running migrations to the current version or a manually specified version. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class Migration |
||||
{ |
||||
/** |
||||
* The OutputWriter object instance used for outputting information |
||||
* |
||||
* @var OutputWriter |
||||
*/ |
||||
private $outputWriter; |
||||
|
||||
/** |
||||
* @var Configuration |
||||
*/ |
||||
private $configuration; |
||||
|
||||
/** |
||||
* Construct a Migration instance |
||||
* |
||||
* @param Configuration $configuration A migration Configuration instance |
||||
*/ |
||||
public function __construct(Configuration $configuration) |
||||
{ |
||||
$this->configuration = $configuration; |
||||
$this->outputWriter = $configuration->getOutputWriter(); |
||||
} |
||||
|
||||
/** |
||||
* Get the array of versions and SQL queries that would be executed for |
||||
* each version but do not execute anything. |
||||
* |
||||
* @param string $to The version to migrate to. |
||||
* |
||||
* @return array $sql The array of SQL queries. |
||||
*/ |
||||
public function getSql($to = null) |
||||
{ |
||||
return $this->migrate($to, true); |
||||
} |
||||
|
||||
/** |
||||
* Write a migration SQL file to the given path |
||||
* |
||||
* @param string $path The path to write the migration SQL file. |
||||
* @param string $to The version to migrate to. |
||||
* |
||||
* @return boolean $written |
||||
*/ |
||||
public function writeSqlFile($path, $to = null) |
||||
{ |
||||
$sql = $this->getSql($to); |
||||
|
||||
$from = $this->configuration->getCurrentVersion(); |
||||
if ($to === null) { |
||||
$to = $this->configuration->getLatestVersion(); |
||||
} |
||||
|
||||
$direction = $from > $to ? 'down' : 'up'; |
||||
|
||||
$this->outputWriter->write(sprintf("# Migrating from %s to %s\n", $from, $to)); |
||||
|
||||
$sqlWriter = new SqlFileWriter( |
||||
$this->configuration->getMigrationsTableName(), |
||||
$path, |
||||
$this->outputWriter |
||||
); |
||||
|
||||
return $sqlWriter->write($sql, $direction); |
||||
} |
||||
|
||||
/** |
||||
* Run a migration to the current version or the given target version. |
||||
* |
||||
* @param string $to The version to migrate to. |
||||
* @param boolean $dryRun Whether or not to make this a dry run and not execute anything. |
||||
* @param boolean $timeAllQueries Measuring or not the execution time of each SQL query. |
||||
* |
||||
* @return array $sql The array of migration sql statements |
||||
* |
||||
* @throws MigrationException |
||||
*/ |
||||
public function migrate($to = null, $dryRun = false, $timeAllQueries = false) |
||||
{ |
||||
/** |
||||
* If no version to migrate to is given we default to the last available one. |
||||
*/ |
||||
if ($to === null) { |
||||
$to = $this->configuration->getLatestVersion(); |
||||
} |
||||
|
||||
$from = (string) $this->configuration->getCurrentVersion(); |
||||
$to = (string) $to; |
||||
|
||||
/** |
||||
* Throw an error if we can't find the migration to migrate to in the registered |
||||
* migrations. |
||||
*/ |
||||
$migrations = $this->configuration->getMigrations(); |
||||
if ( ! isset($migrations[$to]) && $to > 0) { |
||||
throw MigrationException::unknownMigrationVersion($to); |
||||
} |
||||
|
||||
$direction = $from > $to ? 'down' : 'up'; |
||||
$migrationsToExecute = $this->configuration->getMigrationsToExecute($direction, $to); |
||||
|
||||
/** |
||||
* If |
||||
* there are no migrations to execute |
||||
* and there are migrations, |
||||
* and the migration from and to are the same |
||||
* means we are already at the destination return an empty array() |
||||
* to signify that there is nothing left to do. |
||||
*/ |
||||
if ($from === $to && empty($migrationsToExecute) && !empty($migrations)) { |
||||
return array(); |
||||
} |
||||
|
||||
$output = $dryRun ? 'Executing dry run of migration' : 'Migrating'; |
||||
$output .= ' <info>%s</info> to <comment>%s</comment> from <comment>%s</comment>'; |
||||
$this->outputWriter->write(sprintf($output, $direction, $to, $from)); |
||||
|
||||
/** |
||||
* If there are no migrations to execute throw an exception. |
||||
*/ |
||||
if (empty($migrationsToExecute)) { |
||||
throw MigrationException::noMigrationsToExecute(); |
||||
} |
||||
|
||||
$sql = array(); |
||||
$time = 0; |
||||
foreach ($migrationsToExecute as $version) { |
||||
$versionSql = $version->execute($direction, $dryRun, $timeAllQueries); |
||||
$sql[$version->getVersion()] = $versionSql; |
||||
$time += $version->getTime(); |
||||
} |
||||
|
||||
$this->outputWriter->write("\n <comment>------------------------</comment>\n"); |
||||
$this->outputWriter->write(sprintf(" <info>++</info> finished in %s", $time)); |
||||
$this->outputWriter->write(sprintf(" <info>++</info> %s migrations executed", count($migrationsToExecute))); |
||||
$this->outputWriter->write(sprintf(" <info>++</info> %s sql queries", count($sql, true) - count($sql))); |
||||
|
||||
return $sql; |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
/** |
||||
* Class for Migrations specific exceptions |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class MigrationException extends \Exception |
||||
{ |
||||
public static function migrationsNamespaceRequired() |
||||
{ |
||||
return new self('Migrations namespace must be configured in order to use Doctrine migrations.', 2); |
||||
} |
||||
|
||||
public static function migrationsDirectoryRequired() |
||||
{ |
||||
return new self('Migrations directory must be configured in order to use Doctrine migrations.', 3); |
||||
} |
||||
|
||||
public static function noMigrationsToExecute() |
||||
{ |
||||
return new self('Could not find any migrations to execute.', 4); |
||||
} |
||||
|
||||
public static function unknownMigrationVersion($version) |
||||
{ |
||||
return new self(sprintf('Could not find migration version %s', $version), 5); |
||||
} |
||||
|
||||
public static function alreadyAtVersion($version) |
||||
{ |
||||
return new self(sprintf('Database is already at version %s', $version), 6); |
||||
} |
||||
|
||||
public static function duplicateMigrationVersion($version, $class) |
||||
{ |
||||
return new self(sprintf('Migration version %s already registered with class %s', $version, $class), 7); |
||||
} |
||||
|
||||
public static function configurationFileAlreadyLoaded() |
||||
{ |
||||
return new self(sprintf('Migrations configuration file already loaded'), 8); |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
class MigrationsVersion |
||||
{ |
||||
private static $version = 'v1.0.0-alpha3'; |
||||
|
||||
public static function VERSION(){ |
||||
$gitversion = '@git-version@'; |
||||
|
||||
if (self::isACustomPharBuild($gitversion)) { |
||||
return $gitversion; |
||||
} |
||||
return self::$version; |
||||
} |
||||
|
||||
/** |
||||
* @param $gitversion |
||||
* @return bool |
||||
* |
||||
* Check if doctrine migration is installed by composer or |
||||
* in a modified (not tagged) phar version. |
||||
*/ |
||||
private static function isACustomPharBuild($gitversion) { |
||||
return $gitversion !== '@' . 'git-version@'; |
||||
} |
||||
} |
||||
@ -0,0 +1,52 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
/** |
||||
* Simple class for outputting information from migrations. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class OutputWriter |
||||
{ |
||||
private $closure; |
||||
|
||||
public function __construct(\Closure $closure = null) |
||||
{ |
||||
if ($closure === null) { |
||||
$closure = function ($message) {}; |
||||
} |
||||
$this->closure = $closure; |
||||
} |
||||
|
||||
/** |
||||
* Write output using the configured closure. |
||||
* |
||||
* @param string $message The message to write. |
||||
*/ |
||||
public function write($message) |
||||
{ |
||||
$closure = $this->closure; |
||||
$closure($message); |
||||
} |
||||
} |
||||
@ -0,0 +1,82 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Provider; |
||||
|
||||
use Doctrine\ORM\EntityManager; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Doctrine\ORM\Tools\SchemaTool; |
||||
|
||||
/** |
||||
* A schema provider that uses the doctrine ORM to generate schemas. |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
final class OrmSchemaProvider implements SchemaProviderInterface |
||||
{ |
||||
/** |
||||
* @var EntityManagerInterface |
||||
*/ |
||||
private $entityManager; |
||||
|
||||
public function __construct($em) |
||||
{ |
||||
if (!$this->isEntityManager($em)) { |
||||
throw new \InvalidArgumentException(sprintf( |
||||
'$em is not a valid Doctrine ORM Entity Manager, got "%s"', |
||||
is_object($em) ? get_class($em) : gettype($em) |
||||
)); |
||||
} |
||||
|
||||
$this->entityManager = $em; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function createSchema() |
||||
{ |
||||
$metadata = $this->entityManager->getMetadataFactory()->getAllMetadata(); |
||||
if (empty($metadata)) { |
||||
throw new \UnexpectedValueException('No mapping information to process'); |
||||
} |
||||
|
||||
$tool = new SchemaTool($this->entityManager); |
||||
|
||||
return $tool->getSchemaFromMetadata($metadata); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Doctrine's EntityManagerInterface was introduced in version 2.4, since this |
||||
* library allows those older version we need to be able to check for those |
||||
* old ORM versions. Hence the helper method. |
||||
* |
||||
* No need to check to see if EntityManagerInterface exists first here, PHP |
||||
* doesn't care. |
||||
* |
||||
* @param mixed $manager Hopefully an entity manager, but it may be anything |
||||
* @return boolean |
||||
*/ |
||||
private function isEntityManager($manager) |
||||
{ |
||||
return $manager instanceof EntityManagerInterface || $manager instanceof EntityManager; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,36 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Provider; |
||||
|
||||
/** |
||||
* Generates `Schema` objects for the diff command. A schema provider should |
||||
* return the schema to which the database should be migrated. |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
interface SchemaProviderInterface |
||||
{ |
||||
/** |
||||
* Create the schema to which the database should be migrated. |
||||
* |
||||
* @return \Doctrine\DBAL\Schema\Schema |
||||
*/ |
||||
public function createSchema(); |
||||
} |
||||
@ -0,0 +1,48 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Provider; |
||||
|
||||
use Doctrine\DBAL\Schema\Schema; |
||||
|
||||
/** |
||||
* A schemea provider implementation that just returns the schema its given. |
||||
* |
||||
* @since 1.0.0-alpha3 |
||||
*/ |
||||
final class StubSchemaProvider implements SchemaProviderInterface |
||||
{ |
||||
/** |
||||
* @var Schema |
||||
*/ |
||||
private $toSchema; |
||||
|
||||
public function __construct(Schema $schema) |
||||
{ |
||||
$this->toSchema = $schema; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function createSchema() |
||||
{ |
||||
return $this->toSchema; |
||||
} |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
class SkipMigrationException extends MigrationException |
||||
{ |
||||
} |
||||
@ -0,0 +1,121 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
use Doctrine\DBAL\DBALException; |
||||
use Doctrine\DBAL\Exception\InvalidArgumentException; |
||||
|
||||
class SqlFileWriter |
||||
{ |
||||
private $migrationsTableName; |
||||
|
||||
private $destPath; |
||||
|
||||
/** @var null|OutputWriter */ |
||||
private $outputWriter; |
||||
|
||||
/** |
||||
* @param string $migrationsTableName |
||||
* @param string $destPath |
||||
* @param \Doctrine\DBAL\Migrations\OutputWriter $outputWriter |
||||
*/ |
||||
public function __construct($migrationsTableName, $destPath, OutputWriter $outputWriter = null) |
||||
{ |
||||
if (empty($migrationsTableName)) { |
||||
$this->throwInvalidArgumentException('Migrations table name cannot be empty.'); |
||||
} |
||||
$this->migrationsTableName = $migrationsTableName; |
||||
|
||||
if (empty($destPath)) { |
||||
$this->throwInvalidArgumentException('Destination file must be specified.'); |
||||
} |
||||
$this->destPath = $destPath; |
||||
|
||||
$this->outputWriter = $outputWriter; |
||||
} |
||||
|
||||
/** |
||||
* @param array $queriesByVersion array Keys are versions and values are arrays of SQL queries (they must be castable to string) |
||||
* @param string $direction |
||||
* @return int|bool |
||||
*/ |
||||
public function write(array $queriesByVersion, $direction) |
||||
{ |
||||
$path = $this->buildMigrationFilePath(); |
||||
$string = $this->buildMigrationFile($queriesByVersion, $direction); |
||||
|
||||
if ($this->outputWriter) { |
||||
$this->outputWriter->write("\n" . sprintf('Writing migration file to "<info>%s</info>"', $path)); |
||||
} |
||||
|
||||
return file_put_contents($path, $string); |
||||
} |
||||
|
||||
private function buildMigrationFile(array $queriesByVersion, $direction) |
||||
{ |
||||
$string = sprintf("# Doctrine Migration File Generated on %s\n", date('Y-m-d H:i:s')); |
||||
|
||||
foreach ($queriesByVersion as $version => $queries) { |
||||
$string .= "\n# Version " . $version . "\n"; |
||||
foreach ($queries as $query) { |
||||
$string .= $query . ";\n"; |
||||
} |
||||
|
||||
|
||||
$string .= $this->getVersionUpdateQuery($version, $direction); |
||||
} |
||||
|
||||
return $string; |
||||
} |
||||
|
||||
private function getVersionUpdateQuery($version, $direction) |
||||
{ |
||||
if ($direction == 'down') { |
||||
$query = "DELETE FROM %s WHERE version = '%s';\n"; |
||||
} else { |
||||
$query = "INSERT INTO %s (version) VALUES ('%s');\n"; |
||||
} |
||||
|
||||
return sprintf($query, $this->migrationsTableName, $version); |
||||
} |
||||
|
||||
private function buildMigrationFilePath() |
||||
{ |
||||
$path = $this->destPath; |
||||
if (is_dir($path)) { |
||||
$path = realpath($path); |
||||
$path = $path . '/doctrine_migration_' . date('YmdHis') . '.sql'; |
||||
} |
||||
|
||||
return $path; |
||||
} |
||||
|
||||
/** |
||||
* This only exists for backwards-compatibiliy with DBAL 2.4 |
||||
*/ |
||||
protected function throwInvalidArgumentException($message) |
||||
{ |
||||
if (class_exists('Doctrine\DBAL\Exception\InvalidArgumentException')) { |
||||
throw new InvalidArgumentException($message); |
||||
} else { |
||||
throw new DBALException($message); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,181 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
use Doctrine\DBAL\Migrations\OutputWriter; |
||||
use Doctrine\DBAL\Migrations\Tools\Console\Helper\ConfigurationHelper; |
||||
use Symfony\Component\Console\Command\Command; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
use Symfony\Component\Console\Question\ConfirmationQuestion; |
||||
|
||||
/** |
||||
* CLI Command for adding and deleting migration versions from the version table. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
abstract class AbstractCommand extends Command |
||||
{ |
||||
/** |
||||
* The configuration property only contains the configuration injected by the setter. |
||||
* |
||||
* @var Configuration |
||||
*/ |
||||
private $configuration; |
||||
|
||||
/** |
||||
* The migrationConfiguration property contains the configuration |
||||
* created taking into account the command line options. |
||||
* |
||||
* @var Configuration |
||||
*/ |
||||
private $migrationConfiguration; |
||||
|
||||
/** |
||||
* @var OutputWriter |
||||
*/ |
||||
private $outputWriter; |
||||
|
||||
/** |
||||
* @var \Doctrine\DBAL\Connection |
||||
*/ |
||||
private $connection; |
||||
|
||||
protected function configure() |
||||
{ |
||||
$this->addOption('configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a migrations configuration file.'); |
||||
$this->addOption('db-configuration', null, InputOption::VALUE_OPTIONAL, 'The path to a database connection configuration file.'); |
||||
} |
||||
|
||||
protected function outputHeader(Configuration $configuration, OutputInterface $output) |
||||
{ |
||||
$name = $configuration->getName(); |
||||
$name = $name ? $name : 'Doctrine Database Migrations'; |
||||
$name = str_repeat(' ', 20) . $name . str_repeat(' ', 20); |
||||
$output->writeln('<question>' . str_repeat(' ', strlen($name)) . '</question>'); |
||||
$output->writeln('<question>' . $name . '</question>'); |
||||
$output->writeln('<question>' . str_repeat(' ', strlen($name)) . '</question>'); |
||||
$output->writeln(''); |
||||
} |
||||
|
||||
public function setMigrationConfiguration(Configuration $config) |
||||
{ |
||||
$this->configuration = $config; |
||||
} |
||||
|
||||
/** |
||||
* When any (config) command line option is passed to the migration the migrationConfiguration |
||||
* property is set with the new generated configuration. |
||||
* If no (config) option is passed the migrationConfiguration property is set to the value |
||||
* of the configuration one (if any). |
||||
* Else a new configuration is created and assigned to the migrationConfiguration property. |
||||
* |
||||
* @param InputInterface $input |
||||
* @param OutputInterface $output |
||||
* |
||||
* @return Configuration |
||||
*/ |
||||
protected function getMigrationConfiguration(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
if (!$this->migrationConfiguration) { |
||||
$configHelper = new ConfigurationHelper($this->getConnection($input), $this->configuration); |
||||
$this->migrationConfiguration = $configHelper->getMigrationConfig($input, $this->getOutputWriter($output)); |
||||
} |
||||
|
||||
return $this->migrationConfiguration; |
||||
} |
||||
|
||||
/** |
||||
* This method ensure that we stay compatible with symfony console 2.3 by using the deprecated dialog helper |
||||
* but use the ConfirmationQuestion when available. |
||||
* |
||||
* @param $question |
||||
* @param InputInterface $input |
||||
* @param OutputInterface $output |
||||
* @return mixed |
||||
*/ |
||||
protected function askConfirmation($question, InputInterface $input, OutputInterface $output) |
||||
{ |
||||
if ($this->getHelperSet()->has('question')) { |
||||
return $this->getHelper('question')->ask($input, $output, new ConfirmationQuestion($question)); |
||||
} else { |
||||
return $this->getHelper('dialog')->askConfirmation($output, '<question>' . $question . '</question>', false); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output |
||||
* |
||||
* @return \Doctrine\DBAL\Migrations\OutputWriter |
||||
*/ |
||||
private function getOutputWriter(OutputInterface $output) |
||||
{ |
||||
if (!$this->outputWriter) { |
||||
$this->outputWriter = new OutputWriter(function ($message) use ($output) { |
||||
return $output->writeln($message); |
||||
}); |
||||
} |
||||
|
||||
return $this->outputWriter; |
||||
} |
||||
|
||||
/** |
||||
* @param \Symfony\Component\Console\Input\InputInterface $input |
||||
* |
||||
* @return \Doctrine\DBAL\Connection |
||||
* @throws \Doctrine\DBAL\DBALException |
||||
*/ |
||||
private function getConnection(InputInterface $input) |
||||
{ |
||||
if (!$this->connection) { |
||||
if ($input->getOption('db-configuration')) { |
||||
if (!file_exists($input->getOption('db-configuration'))) { |
||||
throw new \InvalidArgumentException("The specified connection file is not a valid file."); |
||||
} |
||||
|
||||
$params = include $input->getOption('db-configuration'); |
||||
if (!is_array($params)) { |
||||
throw new \InvalidArgumentException('The connection file has to return an array with database configuration parameters.'); |
||||
} |
||||
$this->connection = \Doctrine\DBAL\DriverManager::getConnection($params); |
||||
} elseif (file_exists('migrations-db.php')) { |
||||
$params = include 'migrations-db.php'; |
||||
if (!is_array($params)) { |
||||
throw new \InvalidArgumentException('The connection file has to return an array with database configuration parameters.'); |
||||
} |
||||
$this->connection = \Doctrine\DBAL\DriverManager::getConnection($params); |
||||
} elseif ($this->getHelperSet()->has('connection')) { |
||||
$this->connection = $this->getHelper('connection')->getConnection(); |
||||
} elseif ($this->configuration) { |
||||
$this->connection = $this->configuration->getConnection(); |
||||
} else { |
||||
throw new \InvalidArgumentException('You have to specify a --db-configuration file or pass a Database Connection as a dependency to the Migrations.'); |
||||
} |
||||
} |
||||
|
||||
return $this->connection; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,167 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
use Doctrine\DBAL\Version as DbalVersion; |
||||
use Doctrine\DBAL\Migrations\Provider\SchemaProviderInterface; |
||||
use Doctrine\DBAL\Migrations\Provider\OrmSchemaProvider; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
|
||||
/** |
||||
* Command for generate migration classes by comparing your current database schema |
||||
* to your mapping information. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
class DiffCommand extends GenerateCommand |
||||
{ |
||||
/** |
||||
* @var SchemaProviderInterface |
||||
*/ |
||||
protected $schemaProvider; |
||||
|
||||
public function __construct(SchemaProviderInterface $schemaProvider=null) |
||||
{ |
||||
$this->schemaProvider = $schemaProvider; |
||||
parent::__construct(); |
||||
} |
||||
|
||||
protected function configure() |
||||
{ |
||||
parent::configure(); |
||||
|
||||
$this |
||||
->setName('migrations:diff') |
||||
->setDescription('Generate a migration by comparing your current database to your mapping information.') |
||||
->setHelp(<<<EOT |
||||
The <info>%command.name%</info> command generates a migration by comparing your current database to your mapping information: |
||||
|
||||
<info>%command.full_name%</info> |
||||
|
||||
You can optionally specify a <comment>--editor-cmd</comment> option to open the generated file in your favorite editor: |
||||
|
||||
<info>%command.full_name% --editor-cmd=mate</info> |
||||
EOT |
||||
) |
||||
->addOption('filter-expression', null, InputOption::VALUE_OPTIONAL, 'Tables which are filtered by Regular Expression.'); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$isDbalOld = (DbalVersion::compare('2.2.0') > 0); |
||||
$configuration = $this->getMigrationConfiguration($input, $output); |
||||
|
||||
$conn = $configuration->getConnection(); |
||||
$platform = $conn->getDatabasePlatform(); |
||||
|
||||
if ($filterExpr = $input->getOption('filter-expression')) { |
||||
if ($isDbalOld) { |
||||
throw new \InvalidArgumentException('The "--filter-expression" option can only be used as of Doctrine DBAL 2.2'); |
||||
} |
||||
|
||||
$conn->getConfiguration() |
||||
->setFilterSchemaAssetsExpression($filterExpr); |
||||
} |
||||
|
||||
$fromSchema = $conn->getSchemaManager()->createSchema(); |
||||
$toSchema = $this->getSchemaProvider()->createSchema(); |
||||
|
||||
//Not using value from options, because filters can be set from config.yml |
||||
if ( ! $isDbalOld && $filterExpr = $conn->getConfiguration()->getFilterSchemaAssetsExpression()) { |
||||
foreach ($toSchema->getTables() as $table) { |
||||
$tableName = $table->getName(); |
||||
if ( ! preg_match($filterExpr, $this->resolveTableName($tableName))) { |
||||
$toSchema->dropTable($tableName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
$up = $this->buildCodeFromSql($configuration, $fromSchema->getMigrateToSql($toSchema, $platform)); |
||||
$down = $this->buildCodeFromSql($configuration, $fromSchema->getMigrateFromSql($toSchema, $platform)); |
||||
|
||||
if (! $up && ! $down) { |
||||
$output->writeln('No changes detected in your mapping information.', 'ERROR'); |
||||
|
||||
return; |
||||
} |
||||
|
||||
$version = date('YmdHis'); |
||||
$path = $this->generateMigration($configuration, $input, $version, $up, $down); |
||||
|
||||
$output->writeln(sprintf('Generated new migration class to "<info>%s</info>" from schema differences.', $path)); |
||||
} |
||||
|
||||
private function buildCodeFromSql(Configuration $configuration, array $sql) |
||||
{ |
||||
$currentPlatform = $configuration->getConnection()->getDatabasePlatform()->getName(); |
||||
$code = array(); |
||||
foreach ($sql as $query) { |
||||
if (stripos($query, $configuration->getMigrationsTableName()) !== false) { |
||||
continue; |
||||
} |
||||
$code[] = sprintf("\$this->addSql(%s);", var_export($query, true)); |
||||
} |
||||
|
||||
if (!empty($code)) { |
||||
array_unshift( |
||||
$code, |
||||
sprintf( |
||||
"\$this->abortIf(\$this->connection->getDatabasePlatform()->getName() != %s, %s);", |
||||
var_export($currentPlatform, true), |
||||
var_export(sprintf("Migration can only be executed safely on '%s'.", $currentPlatform), true) |
||||
), |
||||
"" |
||||
); |
||||
} |
||||
|
||||
return implode("\n", $code); |
||||
} |
||||
|
||||
private function getSchemaProvider() |
||||
{ |
||||
if (!$this->schemaProvider) { |
||||
$this->schemaProvider = new OrmSchemaProvider($this->getHelper('entityManager')->getEntityManager()); |
||||
} |
||||
|
||||
return $this->schemaProvider; |
||||
} |
||||
|
||||
/** |
||||
* Resolve a table name from its fully qualified name. The `$name` argument |
||||
* comes from Doctrine\DBAL\Schema\Table#getName which can sometimes return |
||||
* a namespaced name with the form `{namespace}.{tableName}`. This extracts |
||||
* the table name from that. |
||||
* |
||||
* @param string $name |
||||
* @return string |
||||
*/ |
||||
private function resolveTableName($name) |
||||
{ |
||||
$pos = strpos($name, '.'); |
||||
|
||||
return false === $pos ? $name : substr($name, $pos + 1); |
||||
} |
||||
} |
||||
@ -0,0 +1,102 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputArgument; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
|
||||
/** |
||||
* Command for executing single migrations up or down manually. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
class ExecuteCommand extends AbstractCommand |
||||
{ |
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setName('migrations:execute') |
||||
->setDescription('Execute a single migration version up or down manually.') |
||||
->addArgument('version', InputArgument::REQUIRED, 'The version to execute.', null) |
||||
->addOption('write-sql', null, InputOption::VALUE_NONE, 'The path to output the migration SQL file instead of executing it.') |
||||
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.') |
||||
->addOption('up', null, InputOption::VALUE_NONE, 'Execute the migration up.') |
||||
->addOption('down', null, InputOption::VALUE_NONE, 'Execute the migration down.') |
||||
->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.') |
||||
->setHelp(<<<EOT |
||||
The <info>%command.name%</info> command executes a single migration version up or down manually: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS</info> |
||||
|
||||
If no <comment>--up</comment> or <comment>--down</comment> option is specified it defaults to up: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --down</info> |
||||
|
||||
You can also execute the migration as a <comment>--dry-run</comment>: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --dry-run</info> |
||||
|
||||
You can output the would be executed SQL statements to a file with <comment>--write-sql</comment>: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --write-sql</info> |
||||
|
||||
Or you can also execute the migration without a warning message which you need to interact with: |
||||
|
||||
<info>%command.full_name% --no-interaction</info> |
||||
EOT |
||||
); |
||||
|
||||
parent::configure(); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$version = $input->getArgument('version'); |
||||
$direction = $input->getOption('down') ? 'down' : 'up'; |
||||
|
||||
$configuration = $this->getMigrationConfiguration($input, $output); |
||||
$version = $configuration->getVersion($version); |
||||
|
||||
$timeAllqueries = $input->getOption('query-time'); |
||||
|
||||
if ($path = $input->getOption('write-sql')) { |
||||
$path = is_bool($path) ? getcwd() : $path; |
||||
$version->writeSqlFile($path, $direction); |
||||
} else { |
||||
if ($input->isInteractive()) { |
||||
$question = 'WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)'; |
||||
$execute = $this->askConfirmation($question, $input, $output); |
||||
} else { |
||||
$execute = true; |
||||
} |
||||
|
||||
if ($execute) { |
||||
$version->execute($direction, (boolean) $input->getOption('dry-run'), $timeAllqueries); |
||||
} else { |
||||
$output->writeln('<error>Migration cancelled!</error>'); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,135 @@ |
||||
<?php |
||||
|
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
|
||||
/** |
||||
* Command for generating new blank migration classes |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
class GenerateCommand extends AbstractCommand |
||||
{ |
||||
|
||||
private static $_template = |
||||
'<?php |
||||
|
||||
namespace <namespace>; |
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration; |
||||
use Doctrine\DBAL\Schema\Schema; |
||||
|
||||
/** |
||||
* Auto-generated Migration: Please modify to your needs! |
||||
*/ |
||||
class Version<version> extends AbstractMigration |
||||
{ |
||||
/** |
||||
* @param Schema $schema |
||||
*/ |
||||
public function up(Schema $schema) |
||||
{ |
||||
// this up() migration is auto-generated, please modify it to your needs |
||||
<up> |
||||
} |
||||
|
||||
/** |
||||
* @param Schema $schema |
||||
*/ |
||||
public function down(Schema $schema) |
||||
{ |
||||
// this down() migration is auto-generated, please modify it to your needs |
||||
<down> |
||||
} |
||||
} |
||||
'; |
||||
|
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setName('migrations:generate') |
||||
->setDescription('Generate a blank migration class.') |
||||
->addOption('editor-cmd', null, InputOption::VALUE_OPTIONAL, 'Open file with this command upon creation.') |
||||
->setHelp(<<<EOT |
||||
The <info>%command.name%</info> command generates a blank migration class: |
||||
|
||||
<info>%command.full_name%</info> |
||||
|
||||
You can optionally specify a <comment>--editor-cmd</comment> option to open the generated file in your favorite editor: |
||||
|
||||
<info>%command.full_name% --editor-cmd=mate</info> |
||||
EOT |
||||
); |
||||
|
||||
parent::configure(); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$configuration = $this->getMigrationConfiguration($input, $output); |
||||
|
||||
$version = date('YmdHis'); |
||||
$path = $this->generateMigration($configuration, $input, $version); |
||||
|
||||
$output->writeln(sprintf('Generated new migration class to "<info>%s</info>"', $path)); |
||||
} |
||||
|
||||
protected function generateMigration(Configuration $configuration, InputInterface $input, $version, $up = null, $down = null) |
||||
{ |
||||
$placeHolders = array( |
||||
'<namespace>', |
||||
'<version>', |
||||
'<up>', |
||||
'<down>' |
||||
); |
||||
$replacements = array( |
||||
$configuration->getMigrationsNamespace(), |
||||
$version, |
||||
$up ? " " . implode("\n ", explode("\n", $up)) : null, |
||||
$down ? " " . implode("\n ", explode("\n", $down)) : null |
||||
); |
||||
$code = str_replace($placeHolders, $replacements, self::$_template); |
||||
$code = preg_replace('/^ +$/m', '', $code); |
||||
$dir = $configuration->getMigrationsDirectory(); |
||||
$dir = $dir ? $dir : getcwd(); |
||||
$dir = rtrim($dir, '/'); |
||||
$path = $dir . '/Version' . $version . '.php'; |
||||
|
||||
if ( ! file_exists($dir)) { |
||||
throw new \InvalidArgumentException(sprintf('Migrations directory "%s" does not exist.', $dir)); |
||||
} |
||||
|
||||
file_put_contents($path, $code); |
||||
|
||||
if ($editorCmd = $input->getOption('editor-cmd')) { |
||||
proc_open($editorCmd . ' ' . escapeshellarg($path), array(), $pipes); |
||||
} |
||||
|
||||
return $path; |
||||
} |
||||
} |
||||
@ -0,0 +1,48 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
|
||||
/** |
||||
* Outputs the latest version number. |
||||
* |
||||
* @author Kris Wallsmith <kris.wallsmith@gmail.com> |
||||
*/ |
||||
class LatestCommand extends AbstractCommand |
||||
{ |
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setName('migrations:latest') |
||||
->setDescription('Outputs the latest version number') |
||||
; |
||||
|
||||
parent::configure(); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$configuration = $this->getMigrationConfiguration($input, $output); |
||||
|
||||
$output->writeln($configuration->getLatestVersion()); |
||||
} |
||||
} |
||||
@ -0,0 +1,192 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
use Doctrine\DBAL\Migrations\Migration; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputArgument; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
|
||||
/** |
||||
* Command for executing a migration to a specified version or the latest available version. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
class MigrateCommand extends AbstractCommand |
||||
{ |
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setName('migrations:migrate') |
||||
->setDescription('Execute a migration to a specified version or the latest available version.') |
||||
->addArgument('version', InputArgument::OPTIONAL, 'The version number (YYYYMMDDHHMMSS) or alias (first, prev, next, latest) to migrate to.', 'latest') |
||||
->addOption('write-sql', null, InputOption::VALUE_NONE, 'The path to output the migration SQL file instead of executing it.') |
||||
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Execute the migration as a dry run.') |
||||
->addOption('query-time', null, InputOption::VALUE_NONE, 'Time all the queries individually.') |
||||
->setHelp(<<<EOT |
||||
The <info>%command.name%</info> command executes a migration to a specified version or the latest available version: |
||||
|
||||
<info>%command.full_name%</info> |
||||
|
||||
You can optionally manually specify the version you wish to migrate to: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS</info> |
||||
|
||||
You can specify the version you wish to migrate to using an alias: |
||||
|
||||
<info>%command.full_name% prev</info> |
||||
|
||||
You can also execute the migration as a <comment>--dry-run</comment>: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --dry-run</info> |
||||
|
||||
You can output the would be executed SQL statements to a file with <comment>--write-sql</comment>: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --write-sql</info> |
||||
|
||||
Or you can also execute the migration without a warning message which you need to interact with: |
||||
|
||||
<info>%command.full_name% --no-interaction</info> |
||||
|
||||
You can also time all the different queries if you wanna know which one is taking so long: |
||||
|
||||
<info>%command.full_name% --query-time</info> |
||||
EOT |
||||
); |
||||
|
||||
parent::configure(); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$configuration = $this->getMigrationConfiguration($input, $output); |
||||
$migration = new Migration($configuration); |
||||
|
||||
$this->outputHeader($configuration, $output); |
||||
|
||||
$timeAllqueries = $input->getOption('query-time'); |
||||
|
||||
$executedMigrations = $configuration->getMigratedVersions(); |
||||
$availableMigrations = $configuration->getAvailableVersions(); |
||||
$executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations); |
||||
|
||||
$version = $this->getVersionNameFromAlias($input->getArgument('version'), $output, $configuration); |
||||
if ($version === false) { |
||||
return 1; |
||||
} |
||||
|
||||
if (!empty($executedUnavailableMigrations)) { |
||||
$output->writeln(sprintf( |
||||
'<error>WARNING! You have %s previously executed migrations' |
||||
. ' in the database that are not registered migrations.</error>', |
||||
count($executedUnavailableMigrations) |
||||
)); |
||||
|
||||
foreach ($executedUnavailableMigrations as $executedUnavailableMigration) { |
||||
$output->writeln(sprintf( |
||||
' <comment>>></comment> %s (<comment>%s</comment>)', |
||||
$configuration->getDateTime($executedUnavailableMigration), |
||||
$executedUnavailableMigration |
||||
)); |
||||
} |
||||
|
||||
$question = 'Are you sure you wish to continue? (y/n)'; |
||||
if (! $this->canExecute($question, $input, $output)) { |
||||
$output->writeln('<error>Migration cancelled!</error>'); |
||||
|
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
if ($path = $input->getOption('write-sql')) { |
||||
$path = is_bool($path) ? getcwd() : $path; |
||||
$migration->writeSqlFile($path, $version); |
||||
} else { |
||||
$dryRun = (boolean) $input->getOption('dry-run'); |
||||
|
||||
// warn the user if no dry run and interaction is on |
||||
if (! $dryRun) { |
||||
$question = 'WARNING! You are about to execute a database migration' |
||||
. ' that could result in schema changes and data lost.' |
||||
. ' Are you sure you wish to continue? (y/n)'; |
||||
if (! $this->canExecute($question, $input, $output)) { |
||||
$output->writeln('<error>Migration cancelled!</error>'); |
||||
|
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
$sql = $migration->migrate($version, $dryRun, $timeAllqueries); |
||||
|
||||
if (empty($sql)) { |
||||
$output->writeln('<comment>No migrations to execute.</comment>'); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* @param string $question |
||||
* @param InputInterface $input |
||||
* @param OutputInterface $output |
||||
* @return bool |
||||
*/ |
||||
private function canExecute($question, InputInterface $input, OutputInterface $output) |
||||
{ |
||||
if ($input->isInteractive() && ! $this->askConfirmation($question, $input, $output)) { |
||||
return false; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* @param string $versionAlias |
||||
* @param OutputInterface $output |
||||
* @param Configuration $configuration |
||||
* @return bool|string |
||||
*/ |
||||
private function getVersionNameFromAlias($versionAlias, OutputInterface $output, Configuration $configuration) |
||||
{ |
||||
$version = $configuration->resolveVersionAlias($versionAlias); |
||||
if ($version === null) { |
||||
if ($versionAlias == 'prev') { |
||||
$output->writeln('<error>Already at first version.</error>'); |
||||
return false; |
||||
} |
||||
if ($versionAlias == 'next') { |
||||
$output->writeln('<error>Already at latest version.</error>'); |
||||
return false; |
||||
} |
||||
|
||||
$output->writeln(sprintf( |
||||
'<error>Unknown version: %s</error>', |
||||
$output->getFormatter()->escape($versionAlias) |
||||
)); |
||||
return false; |
||||
} |
||||
|
||||
return $version; |
||||
} |
||||
} |
||||
@ -0,0 +1,140 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
|
||||
/** |
||||
* Command to view the status of a set of migrations. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
class StatusCommand extends AbstractCommand |
||||
{ |
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setName('migrations:status') |
||||
->setDescription('View the status of a set of migrations.') |
||||
->addOption('show-versions', null, InputOption::VALUE_NONE, 'This will display a list of all available migrations and their status') |
||||
->setHelp(<<<EOT |
||||
The <info>%command.name%</info> command outputs the status of a set of migrations: |
||||
|
||||
<info>%command.full_name%</info> |
||||
|
||||
You can output a list of all available migrations and their status with <comment>--show-versions</comment>: |
||||
|
||||
<info>%command.full_name% --show-versions</info> |
||||
EOT |
||||
); |
||||
|
||||
parent::configure(); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$configuration = $this->getMigrationConfiguration($input, $output); |
||||
|
||||
$formattedVersions = array(); |
||||
foreach (array('prev', 'current', 'next', 'latest') as $alias) { |
||||
$version = $configuration->resolveVersionAlias($alias); |
||||
if ($version === null) { |
||||
if ($alias == 'next') { |
||||
$formattedVersions[$alias] = 'Already at latest version'; |
||||
} elseif ($alias == 'prev') { |
||||
$formattedVersions[$alias] = 'Already at first version'; |
||||
} |
||||
} elseif ($version === '0') { |
||||
$formattedVersions[$alias] = '<comment>0</comment>'; |
||||
} else { |
||||
$formattedVersions[$alias] = $configuration->getDateTime($version) . ' (<comment>' . $version . '</comment>)'; |
||||
} |
||||
} |
||||
|
||||
$executedMigrations = $configuration->getMigratedVersions(); |
||||
$availableMigrations = $configuration->getAvailableVersions(); |
||||
$executedUnavailableMigrations = array_diff($executedMigrations, $availableMigrations); |
||||
$numExecutedUnavailableMigrations = count($executedUnavailableMigrations); |
||||
$newMigrations = count(array_diff($availableMigrations, $executedMigrations)); |
||||
|
||||
$output->writeln("\n <info>==</info> Configuration\n"); |
||||
|
||||
$info = array( |
||||
'Name' => $configuration->getName() ? $configuration->getName() : 'Doctrine Database Migrations', |
||||
'Database Driver' => $configuration->getConnection()->getDriver()->getName(), |
||||
'Database Name' => $configuration->getConnection()->getDatabase(), |
||||
'Configuration Source' => $configuration instanceof \Doctrine\DBAL\Migrations\Configuration\AbstractFileConfiguration ? $configuration->getFile() : 'manually configured', |
||||
'Version Table Name' => $configuration->getMigrationsTableName(), |
||||
'Migrations Namespace' => $configuration->getMigrationsNamespace(), |
||||
'Migrations Directory' => $configuration->getMigrationsDirectory(), |
||||
'Previous Version' => $formattedVersions['prev'], |
||||
'Current Version' => $formattedVersions['current'], |
||||
'Next Version' => $formattedVersions['next'], |
||||
'Latest Version' => $formattedVersions['latest'], |
||||
'Executed Migrations' => count($executedMigrations), |
||||
'Executed Unavailable Migrations' => $numExecutedUnavailableMigrations > 0 ? '<error>'.$numExecutedUnavailableMigrations.'</error>' : 0, |
||||
'Available Migrations' => count($availableMigrations), |
||||
'New Migrations' => $newMigrations > 0 ? '<question>' . $newMigrations . '</question>' : 0 |
||||
); |
||||
foreach ($info as $name => $value) { |
||||
$output->writeln(' <comment>>></comment> ' . $name . ': ' . str_repeat(' ', 50 - strlen($name)) . $value); |
||||
} |
||||
|
||||
if ($input->getOption('show-versions')) { |
||||
if ($migrations = $configuration->getMigrations()) { |
||||
$executedUnavailableMigrations = $migrations; |
||||
$output->writeln("\n <info>==</info> Available Migration Versions\n"); |
||||
|
||||
$this->showVersions($migrations, $configuration, $output); |
||||
} |
||||
|
||||
if (!empty($executedUnavailableMigrations)) { |
||||
$output->writeln("\n <info>==</info> Previously Executed Unavailable Migration Versions\n"); |
||||
$this->showVersions($executedUnavailableMigrations, $configuration, $output); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private function showVersions($migrations, Configuration $configuration, $output) |
||||
{ |
||||
$migratedVersions = $configuration->getMigratedVersions(); |
||||
|
||||
foreach($migrations as $version) { |
||||
$isMigrated = in_array($version->getVersion(), $migratedVersions); |
||||
$status = $isMigrated ? '<info>migrated</info>' : '<error>not migrated</error>'; |
||||
$migrationDescription = ''; |
||||
if ($version->getMigration()->getDescription()) { |
||||
$migrationDescription = str_repeat(' ', 5) . $version->getMigration()->getDescription(); |
||||
} |
||||
$formattedVersion = $configuration->getDateTime($version->getVersion()); |
||||
|
||||
$output->writeln(' <comment>>></comment> ' . $formattedVersion . |
||||
' (<comment>' . $version->getVersion() . '</comment>)' . |
||||
str_repeat(' ', 49 - strlen($formattedVersion) - strlen($version->getVersion())) . |
||||
$status . $migrationDescription); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,177 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Command; |
||||
|
||||
use Doctrine\DBAL\Migrations\MigrationException; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
use Symfony\Component\Console\Output\OutputInterface; |
||||
use Symfony\Component\Console\Input\InputArgument; |
||||
use Symfony\Component\Console\Input\InputOption; |
||||
|
||||
/** |
||||
* Command for manually adding and deleting migration versions from the version table. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan Wage <jonwage@gmail.com> |
||||
*/ |
||||
class VersionCommand extends AbstractCommand |
||||
{ |
||||
/** |
||||
* The Migrations Configuration instance |
||||
* |
||||
* @var \Doctrine\DBAL\Migrations\Configuration\Configuration |
||||
*/ |
||||
private $configuration; |
||||
|
||||
/** |
||||
* Whether or not the versions have to be marked as migrated or not |
||||
* |
||||
* @var boolean |
||||
*/ |
||||
private $markMigrated; |
||||
|
||||
protected function configure() |
||||
{ |
||||
$this |
||||
->setName('migrations:version') |
||||
->setDescription('Manually add and delete migration versions from the version table.') |
||||
->addArgument('version', InputArgument::OPTIONAL, 'The version to add or delete.', null) |
||||
->addOption('add', null, InputOption::VALUE_NONE, 'Add the specified version.') |
||||
->addOption('delete', null, InputOption::VALUE_NONE, 'Delete the specified version.') |
||||
->addOption('all', null, InputOption::VALUE_NONE, 'Apply to all the versions.') |
||||
->addOption('range-from', null, InputOption::VALUE_OPTIONAL, 'Apply from specified version.') |
||||
->addOption('range-to', null, InputOption::VALUE_OPTIONAL, 'Apply to specified version.') |
||||
->setHelp(<<<EOT |
||||
The <info>%command.name%</info> command allows you to manually add, delete or synchronize migration versions from the version table: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --add</info> |
||||
|
||||
If you want to delete a version you can use the <comment>--delete</comment> option: |
||||
|
||||
<info>%command.full_name% YYYYMMDDHHMMSS --delete</info> |
||||
|
||||
If you want to synchronize by adding or deleting all migration versions available in the version table you can use the <comment>--all</comment> option: |
||||
|
||||
<info>%command.full_name% --add --all</info> |
||||
<info>%command.full_name% --delete --all</info> |
||||
|
||||
If you want to synchronize by adding or deleting some range of migration versions available in the version table you can use the <comment>--range-from/--range-to</comment> option: |
||||
|
||||
<info>%command.full_name% --add --range-from=YYYYMMDDHHMMSS --range-to=YYYYMMDDHHMMSS</info> |
||||
<info>%command.full_name% --delete --range-from=YYYYMMDDHHMMSS --range-to=YYYYMMDDHHMMSS</info> |
||||
|
||||
You can also execute this command without a warning message which you need to interact with: |
||||
|
||||
<info>%command.full_name% --no-interaction</info> |
||||
EOT |
||||
); |
||||
|
||||
parent::configure(); |
||||
} |
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output) |
||||
{ |
||||
$this->configuration = $this->getMigrationConfiguration($input, $output); |
||||
|
||||
if ( ! $input->getOption('add') && ! $input->getOption('delete')) { |
||||
throw new \InvalidArgumentException('You must specify whether you want to --add or --delete the specified version.'); |
||||
} |
||||
|
||||
$this->markMigrated = (boolean) $input->getOption('add'); |
||||
|
||||
if ($input->isInteractive()) { |
||||
$question = 'WARNING! You are about to add, delete or synchronize migration versions from the version table that could result in data lost. Are you sure you wish to continue? (y/n)'; |
||||
|
||||
$confirmation = $this->askConfirmation($question, $input, $output); |
||||
|
||||
if ($confirmation) { |
||||
$this->markVersions($input); |
||||
} else { |
||||
$output->writeln('<error>Migration cancelled!</error>'); |
||||
} |
||||
} else { |
||||
$this->markVersions($input); |
||||
} |
||||
|
||||
} |
||||
|
||||
private function markVersions(InputInterface $input) |
||||
{ |
||||
$affectedVersion = $input->getArgument('version'); |
||||
|
||||
$allOption = $input->getOption('all'); |
||||
$rangeFromOption = $input->getOption('range-from'); |
||||
$rangeToOption = $input->getOption('range-to'); |
||||
|
||||
if ($allOption && ($rangeFromOption !== null || $rangeToOption !== null)) { |
||||
throw new \InvalidArgumentException('Options --all and --range-to/--range-from both used. You should use only one of them.'); |
||||
} elseif ($rangeFromOption !== null ^ $rangeToOption !== null) { |
||||
throw new \InvalidArgumentException('Options --range-to and --range-from should be used together.'); |
||||
} |
||||
|
||||
if ($allOption === true) { |
||||
$availableVersions = $this->configuration->getAvailableVersions(); |
||||
foreach ($availableVersions as $version) { |
||||
$this->mark($version, true); |
||||
} |
||||
} elseif ($rangeFromOption !== null && $rangeToOption !== null) { |
||||
$availableVersions = $this->configuration->getAvailableVersions(); |
||||
foreach ($availableVersions as $version) { |
||||
if ($version >= $rangeFromOption && $version <= $rangeToOption) { |
||||
$this->mark($version, true); |
||||
} |
||||
} |
||||
} else { |
||||
$this->mark($affectedVersion); |
||||
} |
||||
} |
||||
|
||||
private function mark($version, $all = false) |
||||
{ |
||||
if ( ! $this->configuration->hasVersion($version)) { |
||||
throw MigrationException::unknownMigrationVersion($version); |
||||
} |
||||
|
||||
$version = $this->configuration->getVersion($version); |
||||
if ($this->markMigrated && $this->configuration->hasVersionMigrated($version)) { |
||||
$marked = true; |
||||
if (! $all) { |
||||
throw new \InvalidArgumentException(sprintf('The version "%s" already exists in the version table.', $version)); |
||||
} |
||||
} |
||||
|
||||
if ( ! $this->markMigrated && ! $this->configuration->hasVersionMigrated($version)) { |
||||
$marked = false; |
||||
if (! $all) { |
||||
throw new \InvalidArgumentException(sprintf('The version "%s" does not exists in the version table.', $version)); |
||||
} |
||||
} |
||||
|
||||
if ( ! isset($marked)) { |
||||
if ($this->markMigrated) { |
||||
$version->markMigrated(); |
||||
} else { |
||||
$version->markNotMigrated(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,107 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations\Tools\Console\Helper; |
||||
|
||||
use Doctrine\DBAL\Connection; |
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
use Doctrine\DBAL\Migrations\OutputWriter; |
||||
use Symfony\Component\Console\Input\InputInterface; |
||||
|
||||
/** |
||||
* Class ConfigurationHelper |
||||
* @package Doctrine\DBAL\Migrations\Tools\Console\Helper |
||||
* @internal |
||||
*/ |
||||
final class ConfigurationHelper |
||||
{ |
||||
|
||||
/** |
||||
* @var Connection |
||||
*/ |
||||
private $connection; |
||||
|
||||
/** |
||||
* @var Configuration |
||||
*/ |
||||
private $configuration; |
||||
|
||||
public function __construct(Connection $connection = null, Configuration $configuration = null) |
||||
{ |
||||
$this->connection = $connection; |
||||
$this->configuration = $configuration; |
||||
} |
||||
|
||||
public function getMigrationConfig(InputInterface $input, OutputWriter $outputWriter) |
||||
{ |
||||
/** |
||||
* If a configuration option is passed to the command line, use that configuration |
||||
* instead of any other one. |
||||
*/ |
||||
if ($input->getOption('configuration')) { |
||||
$outputWriter->write("Loading configuration from command option: " . $input->getOption('configuration')); |
||||
|
||||
return $this->loadConfig($input->getOption('configuration'), $outputWriter); |
||||
} |
||||
|
||||
/** |
||||
* If a configuration has already been set using DI or a Setter use it. |
||||
*/ |
||||
if ($this->configuration) { |
||||
$outputWriter->write("Loading configuration from the integration code of your framework (setter)."); |
||||
|
||||
return $this->configuration; |
||||
} |
||||
|
||||
/** |
||||
* If no any other config has been found, look for default config file in the path. |
||||
*/ |
||||
$defaultConfig = array( |
||||
'migrations.xml', |
||||
'migrations.yml', |
||||
'migrations.yaml', |
||||
); |
||||
foreach ($defaultConfig as $config) { |
||||
if ($this->configExists($config)) { |
||||
$outputWriter->write("Loading configuration from file: $config"); |
||||
|
||||
return $this->loadConfig($config, $outputWriter); |
||||
} |
||||
} |
||||
|
||||
return new Configuration($this->connection, $outputWriter); |
||||
} |
||||
|
||||
|
||||
private function configExists($config) |
||||
{ |
||||
return file_exists($config); |
||||
} |
||||
|
||||
private function loadConfig($config, OutputWriter $outputWriter) |
||||
{ |
||||
$info = pathinfo($config); |
||||
$class = 'Doctrine\DBAL\Migrations\Configuration'; |
||||
$class .= $info['extension'] === 'xml' ? '\XmlConfiguration' : '\YamlConfiguration'; |
||||
$configuration = new $class($this->connection, $outputWriter); |
||||
$configuration->load($config); |
||||
|
||||
return $configuration; |
||||
} |
||||
} |
||||
@ -0,0 +1,392 @@ |
||||
<?php |
||||
/* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
* |
||||
* This software consists of voluntary contributions made by many individuals |
||||
* and is licensed under the LGPL. For more information, see |
||||
* <http://www.doctrine-project.org>. |
||||
*/ |
||||
|
||||
namespace Doctrine\DBAL\Migrations; |
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration; |
||||
|
||||
/** |
||||
* Class which wraps a migration version and allows execution of the |
||||
* individual migration version up or down method. |
||||
* |
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
||||
* @link www.doctrine-project.org |
||||
* @since 2.0 |
||||
* @author Jonathan H. Wage <jonwage@gmail.com> |
||||
*/ |
||||
class Version |
||||
{ |
||||
const STATE_NONE = 0; |
||||
const STATE_PRE = 1; |
||||
const STATE_EXEC = 2; |
||||
const STATE_POST = 3; |
||||
|
||||
/** |
||||
* The Migrations Configuration instance for this migration |
||||
* |
||||
* @var Configuration |
||||
*/ |
||||
private $configuration; |
||||
|
||||
/** |
||||
* The OutputWriter object instance used for outputting information |
||||
* |
||||
* @var OutputWriter |
||||
*/ |
||||
private $outputWriter; |
||||
|
||||
/** |
||||
* The version in timestamp format (YYYYMMDDHHMMSS) |
||||
* |
||||
* @param int |
||||
*/ |
||||
private $version; |
||||
|
||||
/** |
||||
* @var \Doctrine\DBAL\Schema\AbstractSchemaManager |
||||
*/ |
||||
private $sm; |
||||
|
||||
/** |
||||
* @var \Doctrine\DBAL\Platforms\AbstractPlatform |
||||
*/ |
||||
private $platform; |
||||
|
||||
/** |
||||
* The migration instance for this version |
||||
* |
||||
* @var AbstractMigration |
||||
*/ |
||||
private $migration; |
||||
|
||||
/** |
||||
* @var \Doctrine\DBAL\Connection |
||||
*/ |
||||
private $connection; |
||||
|
||||
/** |
||||
* @var string |
||||
*/ |
||||
private $class; |
||||
|
||||
/** The array of collected SQL statements for this version */ |
||||
private $sql = array(); |
||||
|
||||
/** The array of collected parameters for SQL statements for this version */ |
||||
private $params = array(); |
||||
|
||||
/** The array of collected types for SQL statements for this version */ |
||||
private $types = array(); |
||||
|
||||
/** The time in seconds that this migration version took to execute */ |
||||
private $time; |
||||
|
||||
/** |
||||
* @var int |
||||
*/ |
||||
private $state = self::STATE_NONE; |
||||
|
||||
public function __construct(Configuration $configuration, $version, $class) |
||||
{ |
||||
$this->configuration = $configuration; |
||||
$this->outputWriter = $configuration->getOutputWriter(); |
||||
$this->class = $class; |
||||
$this->connection = $configuration->getConnection(); |
||||
$this->sm = $this->connection->getSchemaManager(); |
||||
$this->platform = $this->connection->getDatabasePlatform(); |
||||
$this->migration = new $class($this); |
||||
$this->version = $version; |
||||
} |
||||
|
||||
/** |
||||
* Returns the string version in the format YYYYMMDDHHMMSS |
||||
* |
||||
* @return string $version |
||||
*/ |
||||
public function getVersion() |
||||
{ |
||||
return $this->version; |
||||
} |
||||
|
||||
/** |
||||
* Returns the Migrations Configuration object instance |
||||
* |
||||
* @return Configuration $configuration |
||||
*/ |
||||
public function getConfiguration() |
||||
{ |
||||
return $this->configuration; |
||||
} |
||||
|
||||
/** |
||||
* Check if this version has been migrated or not. |
||||
* |
||||
* @return boolean |
||||
*/ |
||||
public function isMigrated() |
||||
{ |
||||
return $this->configuration->hasVersionMigrated($this); |
||||
} |
||||
|
||||
public function markMigrated() |
||||
{ |
||||
$this->configuration->createMigrationTable(); |
||||
$this->connection->insert( |
||||
$this->configuration->getMigrationsTableName(), |
||||
array('version' => $this->version) |
||||
); |
||||
} |
||||
|
||||
public function markNotMigrated() |
||||
{ |
||||
$this->configuration->createMigrationTable(); |
||||
$this->connection->delete( |
||||
$this->configuration->getMigrationsTableName(), |
||||
array('version' => $this->version) |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* Add some SQL queries to this versions migration |
||||
* |
||||
* @param array|string $sql |
||||
* @param array $params |
||||
* @param array $types |
||||
*/ |
||||
public function addSql($sql, array $params = array(), array $types = array()) |
||||
{ |
||||
if (is_array($sql)) { |
||||
foreach ($sql as $key => $query) { |
||||
$this->sql[] = $query; |
||||
if (isset($params[$key])) { |
||||
$this->params[count($this->sql) - 1] = $params[$key]; |
||||
$this->types[count($this->sql) - 1] = isset($types[$key]) ? $types[$key] : array(); |
||||
} |
||||
} |
||||
} else { |
||||
$this->sql[] = $sql; |
||||
if (!empty($params)) { |
||||
$index = count($this->sql) - 1; |
||||
$this->params[$index] = $params; |
||||
$this->types[$index] = $types; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Write a migration SQL file to the given path |
||||
* |
||||
* @param string $path The path to write the migration SQL file. |
||||
* @param string $direction The direction to execute. |
||||
* |
||||
* @return boolean $written |
||||
*/ |
||||
public function writeSqlFile($path, $direction = 'up') |
||||
{ |
||||
$queries = $this->execute($direction, true); |
||||
|
||||
$this->outputWriter->write("\n# Version " . $this->version . "\n"); |
||||
|
||||
$sqlQueries = [$this->version => $queries]; |
||||
$sqlWriter = new SqlFileWriter( |
||||
$this->configuration->getMigrationsTableName(), |
||||
$path, |
||||
$this->outputWriter |
||||
); |
||||
|
||||
return $sqlWriter->write($sqlQueries, $direction); |
||||
} |
||||
|
||||
/** |
||||
* @return AbstractMigration |
||||
*/ |
||||
public function getMigration() |
||||
{ |
||||
return $this->migration; |
||||
} |
||||
|
||||
/** |
||||
* Execute this migration version up or down and and return the SQL. |
||||
* |
||||
* @param string $direction The direction to execute the migration. |
||||
* @param boolean $dryRun Whether to not actually execute the migration SQL and just do a dry run. |
||||
* @param boolean $timeAllQueries Measuring or not the execution time of each SQL query. |
||||
* |
||||
* @return array $sql |
||||
* |
||||
* @throws \Exception when migration fails |
||||
*/ |
||||
public function execute($direction, $dryRun = false, $timeAllQueries = false) |
||||
{ |
||||
$this->sql = array(); |
||||
|
||||
$transaction = $this->migration->isTransactional(); |
||||
if ($transaction) { |
||||
//only start transaction if in transactional mode |
||||
$this->connection->beginTransaction(); |
||||
} |
||||
|
||||
try { |
||||
$migrationStart = microtime(true); |
||||
|
||||
$this->state = self::STATE_PRE; |
||||
$fromSchema = $this->sm->createSchema(); |
||||
$this->migration->{'pre' . ucfirst($direction)}($fromSchema); |
||||
|
||||
if ($direction === 'up') { |
||||
$this->outputWriter->write("\n" . sprintf(' <info>++</info> migrating <comment>%s</comment>', $this->version) . "\n"); |
||||
} else { |
||||
$this->outputWriter->write("\n" . sprintf(' <info>--</info> reverting <comment>%s</comment>', $this->version) . "\n"); |
||||
} |
||||
|
||||
$this->state = self::STATE_EXEC; |
||||
|
||||
$toSchema = clone $fromSchema; |
||||
$this->migration->$direction($toSchema); |
||||
$this->addSql($fromSchema->getMigrateToSql($toSchema, $this->platform)); |
||||
|
||||
if (! $dryRun) { |
||||
if (!empty($this->sql)) { |
||||
foreach ($this->sql as $key => $query) { |
||||
$queryStart = microtime(true); |
||||
|
||||
if ( ! isset($this->params[$key])) { |
||||
$this->outputWriter->write(' <comment>-></comment> ' . $query); |
||||
$this->connection->exec($query); |
||||
} else { |
||||
$this->outputWriter->write(sprintf(' <comment>-</comment> %s (with parameters)', $query)); |
||||
$this->connection->executeQuery($query, $this->params[$key], $this->types[$key]); |
||||
} |
||||
|
||||
$this->outputQueryTime($queryStart, $timeAllQueries); |
||||
} |
||||
} else { |
||||
$this->outputWriter->write(sprintf( |
||||
'<error>Migration %s was executed but did not result in any SQL statements.</error>', |
||||
$this->version |
||||
)); |
||||
} |
||||
|
||||
if ($direction === 'up') { |
||||
$this->markMigrated(); |
||||
} else { |
||||
$this->markNotMigrated(); |
||||
} |
||||
|
||||
} else { |
||||
foreach ($this->sql as $query) { |
||||
$this->outputWriter->write(' <comment>-></comment> ' . $query); |
||||
} |
||||
} |
||||
|
||||
$this->state = self::STATE_POST; |
||||
$this->migration->{'post' . ucfirst($direction)}($toSchema); |
||||
|
||||
$migrationEnd = microtime(true); |
||||
$this->time = round($migrationEnd - $migrationStart, 2); |
||||
if ($direction === 'up') { |
||||
$this->outputWriter->write(sprintf("\n <info>++</info> migrated (%ss)", $this->time)); |
||||
} else { |
||||
$this->outputWriter->write(sprintf("\n <info>--</info> reverted (%ss)", $this->time)); |
||||
} |
||||
|
||||
if ($transaction) { |
||||
//commit only if running in transactional mode |
||||
$this->connection->commit(); |
||||
} |
||||
|
||||
$this->state = self::STATE_NONE; |
||||
|
||||
return $this->sql; |
||||
} catch (SkipMigrationException $e) { |
||||
if ($transaction) { |
||||
//only rollback transaction if in transactional mode |
||||
$this->connection->rollback(); |
||||
} |
||||
|
||||
if ($dryRun === false) { |
||||
// now mark it as migrated |
||||
if ($direction === 'up') { |
||||
$this->markMigrated(); |
||||
} else { |
||||
$this->markNotMigrated(); |
||||
} |
||||
} |
||||
|
||||
$this->outputWriter->write(sprintf("\n <info>SS</info> skipped (Reason: %s)", $e->getMessage())); |
||||
|
||||
$this->state = self::STATE_NONE; |
||||
|
||||
return array(); |
||||
} catch (\Exception $e) { |
||||
|
||||
$this->outputWriter->write(sprintf( |
||||
'<error>Migration %s failed during %s. Error %s</error>', |
||||
$this->version, $this->getExecutionState(), $e->getMessage() |
||||
)); |
||||
|
||||
if ($transaction) { |
||||
//only rollback transaction if in transactional mode |
||||
$this->connection->rollback(); |
||||
} |
||||
|
||||
$this->state = self::STATE_NONE; |
||||
throw $e; |
||||
} |
||||
} |
||||
|
||||
public function getExecutionState() |
||||
{ |
||||
switch ($this->state) { |
||||
case self::STATE_PRE: |
||||
return 'Pre-Checks'; |
||||
case self::STATE_POST: |
||||
return 'Post-Checks'; |
||||
case self::STATE_EXEC: |
||||
return 'Execution'; |
||||
default: |
||||
return 'No State'; |
||||
} |
||||
} |
||||
|
||||
private function outputQueryTime($queryStart, $timeAllQueries = false) |
||||
{ |
||||
if ($timeAllQueries !== false) { |
||||
$queryEnd = microtime(true); |
||||
$queryTime = round($queryEnd - $queryStart, 4); |
||||
|
||||
$this->outputWriter->write(sprintf(" <info>%ss</info>", $queryTime)); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the time this migration version took to execute |
||||
* |
||||
* @return integer $time The time this migration version took to execute |
||||
*/ |
||||
public function getTime() |
||||
{ |
||||
return $this->time; |
||||
} |
||||
|
||||
public function __toString() |
||||
{ |
||||
return $this->version; |
||||
} |
||||
} |
||||
@ -0,0 +1,37 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
||||
<phpunit colors="true" bootstrap="tests/bootstrap.php"> |
||||
<testsuites> |
||||
<testsuite name="Doctrine2 Database Migrations Test Suite"> |
||||
<directory>./tests/Doctrine/</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
|
||||
<filter> |
||||
<whitelist> |
||||
<directory>./lib/</directory> |
||||
</whitelist> |
||||
</filter> |
||||
|
||||
<listeners> |
||||
<listener class="JohnKary\PHPUnit\Listener\SpeedTrapListener" > |
||||
<arguments> |
||||
<array> |
||||
<element key="slowThreshold"> |
||||
<integer>200</integer> |
||||
</element> |
||||
<element key="reportLength"> |
||||
<integer>10</integer> |
||||
</element> |
||||
</array> |
||||
</arguments> |
||||
</listener> |
||||
</listeners> |
||||
|
||||
<logging> |
||||
<log type="coverage-html" target="build/coverage" title="doctrine migrations" |
||||
charset="UTF-8" yui="true" highlight="true" lowUpperBound="5" |
||||
highLowerBound="70" /> |
||||
<log type="coverage-clover" target="build/logs/clover.xml"/> |
||||
</logging> |
||||
</phpunit> |
||||
Loading…
Reference in new issue