Skip to content

Commit

Permalink
Merge pull request #1045 from daniel-gomes-sociomantic/RollbacksBySta…
Browse files Browse the repository at this point in the history
…rtTime

Rollbacks by start time
  • Loading branch information
rquadling authored Feb 28, 2017
2 parents 7e61e54 + e170d54 commit 9ab56b9
Show file tree
Hide file tree
Showing 16 changed files with 2,200 additions and 251 deletions.
19 changes: 19 additions & 0 deletions docs/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,32 @@ Specifying 0 as the target version will revert all migrations.
$ phinx rollback -e development -t 0
To rollback all migrations to a specific date then use the ``--date``
parameter or ``-d`` for short.

.. code-block:: bash
$ phinx rollback -e development -d 2012
$ phinx rollback -e development -d 201201
$ phinx rollback -e development -d 20120103
$ phinx rollback -e development -d 2012010312
$ phinx rollback -e development -d 201201031205
$ phinx rollback -e development -d 20120103120530
If a breakpoint is set, blocking further rollbacks, you can override the
breakpoint using the ``--force`` parameter or ``-f`` for short.

.. code-block:: bash
$ phinx rollback -e development -t 0 -f
.. note::

When rolling back, Phinx orders the executed migrations using
the order specified in the ``version_order`` option of your
``phinx.yml`` file.
Please see the :doc:`Configuration <configuration>` chapter for more information.

The Status Command
------------------

Expand Down
9 changes: 9 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,12 @@ The aliased classes will still be required to implement the ``Phinx\Migration\Cr
aliases:
permission: \Namespace\Migrations\PermissionMigrationTemplateGenerator
view: \Namespace\Migrations\ViewMigrationTemplateGenerator
Version Order
------

When rolling back or printing the status of migrations, Phinx orders the executed migrations according to the
``version_order`` option, which can have the following values:

* ``creation`` (the default): migrations are ordered by their creation time, which is also part of their filename.
* ``execution``: migrations are ordered by their execution time, also known as start time.
2 changes: 2 additions & 0 deletions phinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ environments:
pass: ''
port: 3306
charset: utf8

version_order: creation
38 changes: 38 additions & 0 deletions src/Phinx/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@
*/
class Config implements ConfigInterface
{
/**
* The value that identifies a version order by creation time.
*/
const VERSION_ORDER_CREATION_TIME = 'creation';

/**
* The value that identifies a version order by execution time.
*/
const VERSION_ORDER_EXECUTION_TIME = 'execution';

/**
* @var array
*/
Expand Down Expand Up @@ -297,6 +307,34 @@ public function getTemplateClass()
return $this->values['templates']['class'];
}

/**
* Get the version order.
*
* @return string
*/
public function getVersionOrder()
{
if (!isset($this->values['version_order'])) {
return self::VERSION_ORDER_CREATION_TIME;
}

return $this->values['version_order'];
}

/**
* Is version order creation time?
*
* @return boolean
*/
public function isVersionOrderCreationTime()
{
$versionOrder = $this->getVersionOrder();

return $versionOrder == self::VERSION_ORDER_CREATION_TIME;
}



/**
* Replace tokens in the specified array.
*
Expand Down
14 changes: 14 additions & 0 deletions src/Phinx/Config/ConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,20 @@ public function getTemplateFile();
*/
public function getTemplateClass();

/**
* Get the version order.
*
* @return string
*/
public function getVersionOrder();

/**
* Is version order creation time?
*
* @return boolean
*/
public function isVersionOrderCreationTime();

/**
* Gets the base class name for migrations.
*
Expand Down
63 changes: 57 additions & 6 deletions src/Phinx/Console/Command/Rollback.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Phinx\Config\Config;

class Rollback extends AbstractCommand
{
Expand Down Expand Up @@ -61,6 +62,10 @@ protected function configure()
If you have a breakpoint set, then you can rollback to target 0 and the rollbacks will stop at the breakpoint.
<info>phinx rollback -e development -t 0 </info>
The <info>version_order</info> configuration option is used to determine the order of the migrations when rolling back.
This can be used to allow the rolling back of the last executed migration instead of the last created one, or combined
with the <info>-d|--date</info> option to rollback to a certain date using the migration start times to order them.
EOT
);
}
Expand All @@ -81,14 +86,16 @@ protected function execute(InputInterface $input, OutputInterface $output)
$date = $input->getOption('date');
$force = !!$input->getOption('force');

$config = $this->getConfig();

if (null === $environment) {
$environment = $this->getConfig()->getDefaultEnvironment();
$environment = $config->getDefaultEnvironment();
$output->writeln('<comment>warning</comment> no environment specified, defaulting to: ' . $environment);
} else {
$output->writeln('<info>using environment</info> ' . $environment);
}

$envOptions = $this->getConfig()->getEnvironment($environment);
$envOptions = $config->getEnvironment($environment);
if (isset($envOptions['adapter'])) {
$output->writeln('<info>using adapter</info> ' . $envOptions['adapter']);
}
Expand All @@ -100,17 +107,61 @@ protected function execute(InputInterface $input, OutputInterface $output)
if (isset($envOptions['name'])) {
$output->writeln('<info>using database</info> ' . $envOptions['name']);
}

$versionOrder = $this->getConfig()->getVersionOrder();
$output->writeln('<info>ordering by </info>' . $versionOrder . " time");

// rollback the specified environment
$start = microtime(true);
if (null !== $date) {
$this->getManager()->rollbackToDateTime($environment, new \DateTime($date), $force);
if (null === $date) {
$targetMustMatchVersion = true;
$target = $version;
} else {
$this->getManager()->rollback($environment, $version, $force);
$targetMustMatchVersion = false;
$target = $this->getTargetFromDate($date);
}

$start = microtime(true);
$this->getManager()->rollback($environment, $target, $force, $targetMustMatchVersion);
$end = microtime(true);

$output->writeln('');
$output->writeln('<comment>All Done. Took ' . sprintf('%.4fs', $end - $start) . '</comment>');
}

/**
* Get Target from Date
*
* @param string $date The date to convert to a target.
* @return string The target
*/
public function getTargetFromDate($date)
{
if (!preg_match('/^\d{4,14}$/', $date)) {
throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
}

// what we need to append to the date according to the possible date string lengths
$dateStrlenToAppend = array(
14 => '',
12 => '00',
10 => '0000',
8 => '000000',
6 => '01000000',
4 => '0101000000',
);

if (!isset($dateStrlenToAppend[strlen($date)])) {
throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
}

$target = $date . $dateStrlenToAppend[strlen($date)];

$dateTime = \DateTime::createFromFormat('YmdHis', $target);

if ($dateTime === false) {
throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].');
}

return $dateTime->format('YmdHis');
}
}
4 changes: 4 additions & 0 deletions src/Phinx/Console/Command/Status.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ protected function configure()
<info>phinx status -e development</info>
<info>phinx status -e development -f json</info>
The <info>version_order</info> configuration option is used to determine the order of the status migrations.
EOT
);
}
Expand Down Expand Up @@ -80,6 +82,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->writeln('<info>using format</info> ' . $format);
}

$output->writeln('<info>ordering by </info>' . $this->getConfig()->getVersionOrder() . " time");

// print the status
return $this->getManager()->printStatus($environment, $format);
}
Expand Down
5 changes: 3 additions & 2 deletions src/Phinx/Db/Adapter/AdapterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ interface AdapterInterface
public function getVersions();

/**
* Get all migration log entries, indexed by version number.
*
* Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's
* version order option
*
* @return array
*/
public function getVersionLog();
Expand Down
16 changes: 14 additions & 2 deletions src/Phinx/Db/Adapter/PdoAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -408,8 +408,20 @@ public function getVersions()
public function getVersionLog()
{
$result = array();
$rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY version ASC', $this->getSchemaTableName()));
foreach ($rows as $version) {

switch ($this->options['version_order']) {
case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME:
$orderBy = 'version ASC';
break;
case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME:
$orderBy = 'start_time ASC, version ASC';
break;
default:
throw new \RuntimeException('Invalid version_order configuration option');
}

$rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->getSchemaTableName(), $orderBy));
foreach($rows as $version) {
$result[$version['version']] = $version;
}

Expand Down
Loading

0 comments on commit 9ab56b9

Please sign in to comment.