Initial commit: apps.prometeysev.ru
This commit is contained in:
commit
96ccabcd8b
6561 files changed
+1270349
No files matched your search
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell;
|
||||
|
||||
use Cake\Console\Shell;
|
||||
use Migrations\MigrationsDispatcher;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
|
||||
/**
|
||||
* A wrapper shell for phinx migrations, used to inject our own
|
||||
* console actions so that database configuration already defined
|
||||
* for the application can be reused.
|
||||
*
|
||||
* @property \Migrations\Shell\Task\CreateTask $Create
|
||||
* @property \Migrations\Shell\Task\DumpTask $Dump
|
||||
* @property \Migrations\Shell\Task\MarkMigratedTask $MarkMigrated
|
||||
* @property \Migrations\Shell\Task\MigrateTask $Migrate
|
||||
* @property \Migrations\Shell\Task\RollbackTask $Rollback
|
||||
* @property \Migrations\Shell\Task\StatusTask $Status
|
||||
*/
|
||||
class MigrationsShell extends Shell
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public $tasks = [
|
||||
'Migrations.Create',
|
||||
'Migrations.Dump',
|
||||
'Migrations.MarkMigrated',
|
||||
'Migrations.Migrate',
|
||||
'Migrations.Rollback',
|
||||
'Migrations.Status',
|
||||
];
|
||||
|
||||
/**
|
||||
* Array of arguments to run the shell with.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $argv = [];
|
||||
|
||||
/**
|
||||
* Defines what options can be passed to the shell.
|
||||
* This is required because CakePHP validates the passed options
|
||||
* and would complain if something not configured here is present
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
return parent::getOptionParser()
|
||||
->addOption('plugin', ['short' => 'p'])
|
||||
->addOption('target', ['short' => 't'])
|
||||
->addOption('connection', ['short' => 'c'])
|
||||
->addOption('source', ['short' => 's'])
|
||||
->addOption('seed')
|
||||
->addOption('ansi')
|
||||
->addOption('no-ansi')
|
||||
->addOption('no-lock', ['boolean' => true])
|
||||
->addOption('force', ['boolean' => true])
|
||||
->addOption('version', ['short' => 'V'])
|
||||
->addOption('no-interaction', ['short' => 'n'])
|
||||
->addOption('template', ['short' => 't'])
|
||||
->addOption('format', ['short' => 'f'])
|
||||
->addOption('only', ['short' => 'o'])
|
||||
->addOption('dry-run', ['short' => 'x'])
|
||||
->addOption('exclude', ['short' => 'e']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines constants that are required by phinx to get running
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize()
|
||||
{
|
||||
if (!defined('PHINX_VERSION')) {
|
||||
define('PHINX_VERSION', 'UNKNOWN');
|
||||
}
|
||||
parent::initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* This acts as a front-controller for phinx. It just instantiates the classes
|
||||
* responsible for parsing the command line from phinx and gives full control of
|
||||
* the rest of the flow to it.
|
||||
*
|
||||
* The input parameter of the ``MigrationDispatcher::run()`` method is manually built
|
||||
* in case a MigrationsShell is dispatched using ``Shell::dispatch()``.
|
||||
*
|
||||
* @return bool Success of the call.
|
||||
*/
|
||||
public function main()
|
||||
{
|
||||
$app = $this->getApp();
|
||||
$input = new ArgvInput($this->argv);
|
||||
$app->setAutoExit(false);
|
||||
$exitCode = $app->run($input, $this->getOutput());
|
||||
|
||||
if (in_array('-h', $this->argv) || in_array('--help', $this->argv)) {
|
||||
return $exitCode === 0;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($this->argv[1]) && in_array($this->argv[1], ['migrate', 'rollback']) &&
|
||||
!$this->params['no-lock'] &&
|
||||
$exitCode === 0
|
||||
) {
|
||||
$dispatchCommand = 'migrations dump';
|
||||
if (!empty($this->params['connection'])) {
|
||||
$dispatchCommand .= ' -c ' . $this->params['connection'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$dispatchCommand .= ' -p ' . $this->params['plugin'];
|
||||
}
|
||||
|
||||
$dumpExitCode = $this->dispatchShell($dispatchCommand);
|
||||
}
|
||||
|
||||
if (isset($dumpExitCode) && $exitCode === 0 && $dumpExitCode !== 0) {
|
||||
$exitCode = 1;
|
||||
}
|
||||
|
||||
return $exitCode === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MigrationsDispatcher the Shell will have to use
|
||||
*
|
||||
* @return \Migrations\MigrationsDispatcher
|
||||
*/
|
||||
protected function getApp()
|
||||
{
|
||||
return new MigrationsDispatcher(PHINX_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of OutputInterface the MigrationsDispatcher will have to use.
|
||||
*
|
||||
* @return \Symfony\Component\Console\Output\ConsoleOutput
|
||||
*/
|
||||
protected function getOutput()
|
||||
{
|
||||
return new ConsoleOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default behavior to save the command called
|
||||
* in order to pass it to the command dispatcher
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function runCommand($argv, $autoMethod = false, $extra = [])
|
||||
{
|
||||
array_unshift($argv, 'migrations');
|
||||
$this->argv = $argv;
|
||||
|
||||
return parent::runCommand($argv, $autoMethod, $extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the help in the correct format
|
||||
*
|
||||
* @param string $command The command to get help for.
|
||||
* @return int|bool|null Exit code or number of bytes written to stdout
|
||||
*/
|
||||
protected function displayHelp($command)
|
||||
{
|
||||
return $this->main();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
// @codingStandardsIgnoreStart
|
||||
protected function _displayHelp($command)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
return $this->displayHelp($command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Cake\Console\Shell;
|
||||
|
||||
/**
|
||||
* Base Task that Migrations subcommands Task class should extend.
|
||||
* It implements the getOptionParser method that defines the common options
|
||||
* for all subcommands.
|
||||
*/
|
||||
class CommandTask extends Shell
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addOption('plugin', [
|
||||
'short' => 'p',
|
||||
'help' => 'The plugin the command should be applied to',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('connection', [
|
||||
'short' => 'c',
|
||||
'help' => 'The datasource connection to use',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('source', [
|
||||
'short' => 's',
|
||||
'help' => 'The name of the folder where migrations are stored',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('ansi')
|
||||
->addOption('no-ansi')
|
||||
->addOption('no-interaction', ['short' => 'n']);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Phinx\Console\Command\Create;
|
||||
|
||||
/**
|
||||
* This task class is needed in order to provide a correct autocompletion feature
|
||||
* when using the CakePHP migrations shell plugin. It has no effect on the
|
||||
* migrations process.
|
||||
*/
|
||||
class CreateTask extends CommandTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addArgument('name', [
|
||||
'help' => 'What is the name of the migration?',
|
||||
])
|
||||
->addOption('template', [
|
||||
'short' => 't',
|
||||
'help' => 'Use an alternative template',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('class', [
|
||||
'short' => 'l',
|
||||
'help' => 'Use a class implementing "' . Create::CREATION_INTERFACE . '" to generate the template',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
/**
|
||||
* This task class is needed in order to provide a correct autocompletion feature
|
||||
* when using the CakePHP migrations shell plugin. It has no effect on the
|
||||
* migrations process.
|
||||
*/
|
||||
class DumpTask extends CommandTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->setDescription('Dumps the current schema of the database to be used while baking a diff');
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
/**
|
||||
* This task class is needed in order to provide a correct autocompletion feature
|
||||
* when using the CakePHP migrations shell plugin. It has no effect on the
|
||||
* migrations process.
|
||||
*/
|
||||
class MarkMigratedTask extends CommandTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addArgument('version', [
|
||||
'help' => 'What is the version of the migration?',
|
||||
])
|
||||
->addOption('exclude', [
|
||||
'short' => 'x',
|
||||
'help' => 'If present it will mark migrations from beginning until the given version, excluding it',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('only', [
|
||||
'short' => 'o',
|
||||
'help' => 'If present it will only mark the given migration version',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
/**
|
||||
* This task class is needed in order to provide a correct autocompletion feature
|
||||
* when using the CakePHP migrations shell plugin. It has no effect on the
|
||||
* migrations process.
|
||||
*/
|
||||
class MigrateTask extends CommandTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addOption('target', [
|
||||
'short' => 't',
|
||||
'help' => 'The version number to migrate to',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('date', [
|
||||
'short' => 'd',
|
||||
'help' => 'The date to migrate to',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('dry-run', ['short' => 'x'])
|
||||
->addOption('fake', ['boolean' => true])
|
||||
->addOption('no-lock', [
|
||||
'help' => 'If present, no lock file will be generated after migrating',
|
||||
'boolean' => true,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Database\Schema\Table;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Event\EventManager;
|
||||
use Migrations\Util\UtilTrait;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
|
||||
/**
|
||||
* Task class for generating migration diff files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class MigrationDiffTask extends SimpleMigrationTask
|
||||
{
|
||||
use SnapshotTrait;
|
||||
use UtilTrait;
|
||||
|
||||
/**
|
||||
* Array of migrations that have already been migrated
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migratedItems = [];
|
||||
|
||||
/**
|
||||
* Path to the migration files
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $migrationsPath;
|
||||
|
||||
/**
|
||||
* Migration files that are stored in the self::migrationsPath
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrationsFiles = [];
|
||||
|
||||
/**
|
||||
* Name of the phinx log table
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $phinxTable;
|
||||
|
||||
/**
|
||||
* List the tables the connection currently holds
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tables = [];
|
||||
|
||||
/**
|
||||
* Array of \Cake\Database\Schema\Table objects from the dump file which
|
||||
* represents the state of the database after the last migrate / rollback command
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dumpSchema;
|
||||
|
||||
/**
|
||||
* Array of \Cake\Database\Schema\Table objects from the current state of the database
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $currentSchema;
|
||||
|
||||
/**
|
||||
* List of the tables that are commonly found in the dump schema and the current schema
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commonTables;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected $templateData = [];
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$this->setup();
|
||||
|
||||
if (!$this->checkSync()) {
|
||||
$this->abort('Your migrations history is not in sync with your migrations files. ' .
|
||||
'Make sure all your migrations have been migrated before baking a diff.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (empty($this->migrationsFiles) && empty($this->migratedItems)) {
|
||||
return $this->bakeSnapshot($name);
|
||||
}
|
||||
|
||||
$collection = $this->getCollection($this->connection);
|
||||
EventManager::instance()->on('Bake.initialize', function (Event $event) use ($collection) {
|
||||
$event->getSubject()->loadHelper('Migrations.Migration', [
|
||||
'collection' => $collection,
|
||||
]);
|
||||
});
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up everything the baking process needs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setup()
|
||||
{
|
||||
$this->migrationsPath = $this->getPath();
|
||||
$this->migrationsFiles = glob($this->migrationsPath . '*.php');
|
||||
$this->phinxTable = $this->getPhinxTable($this->plugin);
|
||||
|
||||
$connection = ConnectionManager::get($this->connection);
|
||||
$this->tables = $connection->getSchemaCollection()->listTables();
|
||||
$tableExists = in_array($this->phinxTable, $this->tables);
|
||||
|
||||
$migratedItems = [];
|
||||
if ($tableExists) {
|
||||
$query = $connection->newQuery();
|
||||
$migratedItems = $query
|
||||
->select(['version'])
|
||||
->from($this->phinxTable)
|
||||
->order(['version DESC'])
|
||||
->execute()->fetchAll('assoc');
|
||||
}
|
||||
|
||||
$this->migratedItems = $migratedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection from a database.
|
||||
*
|
||||
* @param string $connection Database connection name.
|
||||
* @return \Cake\Database\Schema\Collection
|
||||
*/
|
||||
public function getCollection($connection)
|
||||
{
|
||||
$connection = ConnectionManager::get($connection);
|
||||
|
||||
return $connection->getSchemaCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process and prepare the data needed for the bake template to be generated.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function templateData()
|
||||
{
|
||||
$this->dumpSchema = $this->getDumpSchema();
|
||||
$this->currentSchema = $this->getCurrentSchema();
|
||||
$this->commonTables = array_intersect_key($this->currentSchema, $this->dumpSchema);
|
||||
|
||||
$this->calculateDiff();
|
||||
|
||||
return [
|
||||
'data' => $this->templateData,
|
||||
'dumpSchema' => $this->dumpSchema,
|
||||
'currentSchema' => $this->currentSchema,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods runs the various methods needed to calculate a diff between the current
|
||||
* state of the database and the schema dump file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function calculateDiff()
|
||||
{
|
||||
$this->getConstraints();
|
||||
$this->getIndexes();
|
||||
$this->getColumns();
|
||||
$this->getTables();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the diff between the current state of the database and the schema dump
|
||||
* by returning an array containing the full \Cake\Database\Schema\Table definitions
|
||||
* of tables to be created and removed in the diff file.
|
||||
*
|
||||
* The method directly sets the diff in a property of the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getTables()
|
||||
{
|
||||
$this->templateData['fullTables'] = [
|
||||
'add' => array_diff_key($this->currentSchema, $this->dumpSchema),
|
||||
'remove' => array_diff_key($this->dumpSchema, $this->currentSchema),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the diff between columns in existing tables.
|
||||
* This will look for columns addition, columns removal and changes in columns metadata
|
||||
* such as change of types or property such as length.
|
||||
*
|
||||
* Note that the method is not able to detect columns name change.
|
||||
* The method directly sets the diff in a property of the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getColumns()
|
||||
{
|
||||
foreach ($this->commonTables as $table => $currentSchema) {
|
||||
$currentColumns = $currentSchema->columns();
|
||||
$oldColumns = $this->dumpSchema[$table]->columns();
|
||||
|
||||
// brand new columns
|
||||
$addedColumns = array_diff($currentColumns, $oldColumns);
|
||||
foreach ($addedColumns as $columnName) {
|
||||
$column = $currentSchema->getColumn($columnName);
|
||||
$key = array_search($columnName, $currentColumns);
|
||||
if ($key > 0) {
|
||||
$column['after'] = $currentColumns[$key - 1];
|
||||
}
|
||||
if (isset($column['unsigned'])) {
|
||||
$column['signed'] = !$column['unsigned'];
|
||||
unset($column['unsigned']);
|
||||
}
|
||||
$this->templateData[$table]['columns']['add'][$columnName] = $column;
|
||||
}
|
||||
|
||||
// changes in columns meta-data
|
||||
foreach ($currentColumns as $columnName) {
|
||||
$column = $currentSchema->getColumn($columnName);
|
||||
$oldColumn = $this->dumpSchema[$table]->getColumn($columnName);
|
||||
unset($column['collate']);
|
||||
unset($oldColumn['collate']);
|
||||
|
||||
if (
|
||||
in_array($columnName, $oldColumns) &&
|
||||
$column !== $oldColumn
|
||||
) {
|
||||
$changedAttributes = array_diff_assoc($column, $oldColumn);
|
||||
|
||||
foreach (['type', 'length', 'null', 'default'] as $attribute) {
|
||||
$phinxAttributeName = $attribute;
|
||||
if ($attribute == 'length') {
|
||||
$phinxAttributeName = 'limit';
|
||||
}
|
||||
if (!isset($changedAttributes[$phinxAttributeName])) {
|
||||
$changedAttributes[$phinxAttributeName] = $column[$attribute];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($changedAttributes['unsigned'])) {
|
||||
$changedAttributes['signed'] = !$changedAttributes['unsigned'];
|
||||
unset($changedAttributes['unsigned']);
|
||||
} else {
|
||||
// badish hack
|
||||
if (isset($column['unsigned']) && $column['unsigned'] === true) {
|
||||
$changedAttributes['signed'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($changedAttributes['length'])) {
|
||||
if (!isset($changedAttributes['limit'])) {
|
||||
$changedAttributes['limit'] = $changedAttributes['length'];
|
||||
}
|
||||
|
||||
unset($changedAttributes['length']);
|
||||
}
|
||||
|
||||
$this->templateData[$table]['columns']['changed'][$columnName] = $changedAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
// columns deletion
|
||||
if (!isset($this->templateData[$table]['columns']['remove'])) {
|
||||
$this->templateData[$table]['columns']['remove'] = [];
|
||||
}
|
||||
$removedColumns = array_diff($oldColumns, $currentColumns);
|
||||
if (!empty($removedColumns)) {
|
||||
foreach ($removedColumns as $columnName) {
|
||||
$column = $this->dumpSchema[$table]->getColumn($columnName);
|
||||
$key = array_search($columnName, $oldColumns);
|
||||
if ($key > 0) {
|
||||
$column['after'] = $oldColumns[$key - 1];
|
||||
}
|
||||
$this->templateData[$table]['columns']['remove'][$columnName] = $column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the diff between contraints in existing tables.
|
||||
* This will look for contraints addition, contraints removal and changes in contraints metadata
|
||||
* such as change of referenced columns if the old constraints and the new one have the same name.
|
||||
*
|
||||
* The method directly sets the diff in a property of the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getConstraints()
|
||||
{
|
||||
foreach ($this->commonTables as $table => $currentSchema) {
|
||||
$currentConstraints = $currentSchema->constraints();
|
||||
$oldConstraints = $this->dumpSchema[$table]->constraints();
|
||||
|
||||
// brand new constraints
|
||||
$addedConstraints = array_diff($currentConstraints, $oldConstraints);
|
||||
foreach ($addedConstraints as $constraintName) {
|
||||
$this->templateData[$table]['constraints']['add'][$constraintName] =
|
||||
$currentSchema->getConstraint($constraintName);
|
||||
$constraint = $currentSchema->getConstraint($constraintName);
|
||||
if ($constraint['type'] === Table::CONSTRAINT_FOREIGN) {
|
||||
$this->templateData[$table]['constraints']['add'][$constraintName] = $constraint;
|
||||
} else {
|
||||
$this->templateData[$table]['indexes']['add'][$constraintName] = $constraint;
|
||||
}
|
||||
}
|
||||
|
||||
// constraints having the same name between new and old schema
|
||||
// if present in both, check if they are the same : if not, remove the old one and add the new one
|
||||
foreach ($currentConstraints as $constraintName) {
|
||||
$constraint = $currentSchema->getConstraint($constraintName);
|
||||
|
||||
if (
|
||||
in_array($constraintName, $oldConstraints) &&
|
||||
$constraint !== $this->dumpSchema[$table]->getConstraint($constraintName)
|
||||
) {
|
||||
$this->templateData[$table]['constraints']['remove'][$constraintName] =
|
||||
$this->dumpSchema[$table]->getConstraint($constraintName);
|
||||
$this->templateData[$table]['constraints']['add'][$constraintName] =
|
||||
$constraint;
|
||||
}
|
||||
}
|
||||
|
||||
// removed constraints
|
||||
$removedConstraints = array_diff($oldConstraints, $currentConstraints);
|
||||
foreach ($removedConstraints as $constraintName) {
|
||||
$constraint = $this->dumpSchema[$table]->getConstraint($constraintName);
|
||||
if ($constraint['type'] === Table::CONSTRAINT_FOREIGN) {
|
||||
$this->templateData[$table]['constraints']['remove'][$constraintName] = $constraint;
|
||||
} else {
|
||||
$this->templateData[$table]['indexes']['remove'][$constraintName] = $constraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the diff between indexes in existing tables.
|
||||
* This will look for indexes addition, indexes removal and changes in indexes metadata
|
||||
* such as change of referenced columns if the old indexes and the new one have the same name.
|
||||
*
|
||||
* The method directly sets the diff in a property of the class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function getIndexes()
|
||||
{
|
||||
foreach ($this->commonTables as $table => $currentSchema) {
|
||||
$currentIndexes = $currentSchema->indexes();
|
||||
$oldIndexes = $this->dumpSchema[$table]->indexes();
|
||||
sort($currentIndexes);
|
||||
sort($oldIndexes);
|
||||
|
||||
// brand new indexes
|
||||
$addedIndexes = array_diff($currentIndexes, $oldIndexes);
|
||||
foreach ($addedIndexes as $indexName) {
|
||||
$this->templateData[$table]['indexes']['add'][$indexName] = $currentSchema->getIndex($indexName);
|
||||
}
|
||||
|
||||
// indexes having the same name between new and old schema
|
||||
// if present in both, check if they are the same : if not, remove the old one and add the new one
|
||||
foreach ($currentIndexes as $indexName) {
|
||||
$index = $currentSchema->getIndex($indexName);
|
||||
|
||||
if (
|
||||
in_array($indexName, $oldIndexes) &&
|
||||
$index !== $this->dumpSchema[$table]->getIndex($indexName)
|
||||
) {
|
||||
$this->templateData[$table]['indexes']['remove'][$indexName] =
|
||||
$this->dumpSchema[$table]->getIndex($indexName);
|
||||
$this->templateData[$table]['indexes']['add'][$indexName] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
// indexes deletion
|
||||
if (!isset($this->templateData[$table]['indexes']['remove'])) {
|
||||
$this->templateData[$table]['indexes']['remove'] = [];
|
||||
}
|
||||
|
||||
$removedIndexes = array_diff($oldIndexes, $currentIndexes);
|
||||
$parts = [];
|
||||
if (!empty($removedIndexes)) {
|
||||
foreach ($removedIndexes as $index) {
|
||||
$parts[$index] = $this->dumpSchema[$table]->getIndex($index);
|
||||
}
|
||||
}
|
||||
$this->templateData[$table]['indexes']['remove'] = array_merge(
|
||||
$this->templateData[$table]['indexes']['remove'],
|
||||
$parts
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the migrations history is in sync with the migrations files
|
||||
*
|
||||
* @return bool Whether migrations history is sync or not
|
||||
*/
|
||||
protected function checkSync()
|
||||
{
|
||||
if (empty($this->migrationsFiles) && empty($this->migratedItems)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!empty($this->migratedItems)) {
|
||||
$lastVersion = $this->migratedItems[0]['version'];
|
||||
$lastFile = end($this->migrationsFiles);
|
||||
|
||||
return (bool)strpos($lastFile, (string)$lastVersion);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback method called to bake a snapshot when the phinx log history is empty and
|
||||
* there are no migration files.
|
||||
*
|
||||
* @param string $name Name.
|
||||
* @return int Value of the snapshot baking dispatch process
|
||||
*/
|
||||
protected function bakeSnapshot($name)
|
||||
{
|
||||
$this->out('Your migrations history is empty and you do not have any migrations files.');
|
||||
$this->out('Falling back to baking a snapshot...');
|
||||
$dispatchCommand = 'bake migration_snapshot ' . $name;
|
||||
|
||||
if (!empty($this->params['connection'])) {
|
||||
$dispatchCommand .= ' -c ' . $this->params['connection'];
|
||||
}
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$dispatchCommand .= ' -p ' . $this->params['plugin'];
|
||||
}
|
||||
|
||||
$dispatch = $this->dispatchShell([
|
||||
'command' => $dispatchCommand,
|
||||
]);
|
||||
|
||||
if ($dispatch === 1) {
|
||||
$this->abort('Something went wrong during the snapshot baking. Please try again.');
|
||||
}
|
||||
|
||||
return $dispatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the correct schema dump based on the arguments and options passed to the shell call
|
||||
* and returns it as an array
|
||||
*
|
||||
* @return array Full database schema : the key is the name of the table and the value is
|
||||
* an instance of \Cake\Database\Schema\Table.
|
||||
*/
|
||||
protected function getDumpSchema()
|
||||
{
|
||||
$inputArgs = [];
|
||||
|
||||
$connectionName = 'default';
|
||||
if (!empty($this->params['connection'])) {
|
||||
$connectionName = $inputArgs['--connection'] = $this->params['connection'];
|
||||
}
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$inputArgs['--plugin'] = $this->params['plugin'];
|
||||
}
|
||||
|
||||
$className = '\Migrations\Command\Dump';
|
||||
$definition = (new $className())->getDefinition();
|
||||
|
||||
$input = new ArrayInput($inputArgs, $definition);
|
||||
$path = $this->getOperationsPath($input) . DS . 'schema-dump-' . $connectionName . '.lock';
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$msg = 'Unable to retrieve the schema dump file. You can create a dump file using ' .
|
||||
'the `cake migrations dump` command';
|
||||
$this->abort($msg);
|
||||
}
|
||||
|
||||
return unserialize(file_get_contents($path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflects the current database schema.
|
||||
*
|
||||
* @return array Full database schema : the key is the name of the table and the value is
|
||||
* an instance of \Cake\Database\Schema\Table.
|
||||
*/
|
||||
protected function getCurrentSchema()
|
||||
{
|
||||
$schema = [];
|
||||
|
||||
if (empty($this->tables)) {
|
||||
return $schema;
|
||||
}
|
||||
|
||||
$collection = ConnectionManager::get($this->connection)->getSchemaCollection();
|
||||
foreach ($this->tables as $table) {
|
||||
if (preg_match("/^.*phinxlog$/", $table) === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$schema[$table] = $collection->describe($table);
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Migrations.config/diff';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->addArgument('name', [
|
||||
'help' => 'Name of the migration to bake. Can use Plugin.name to bake migration files into plugins.',
|
||||
'required' => true,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Event\EventManager;
|
||||
use Migrations\TableFinderTrait;
|
||||
use Migrations\Util\UtilTrait;
|
||||
|
||||
/**
|
||||
* Task class for generating migration snapshot files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class MigrationSnapshotTask extends SimpleMigrationTask
|
||||
{
|
||||
use SnapshotTrait;
|
||||
use TableFinderTrait;
|
||||
use UtilTrait;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$collection = $this->getCollection($this->connection);
|
||||
EventManager::instance()->on('Bake.initialize', function (Event $event) use ($collection) {
|
||||
$event->getSubject()->loadHelper('Migrations.Migration', [
|
||||
'collection' => $collection,
|
||||
]);
|
||||
});
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Migrations.config/snapshot';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function templateData()
|
||||
{
|
||||
$namespace = Configure::read('App.namespace');
|
||||
$pluginPath = '';
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->_pluginNamespace($this->plugin);
|
||||
$pluginPath = $this->plugin . '.';
|
||||
}
|
||||
|
||||
$collection = $this->getCollection($this->connection);
|
||||
$options = [
|
||||
'require-table' => $this->params['require-table'],
|
||||
'plugin' => $this->plugin,
|
||||
];
|
||||
$tables = $this->getTablesToBake($collection, $options);
|
||||
|
||||
sort($tables, SORT_NATURAL);
|
||||
|
||||
$tables = array_combine($tables, $tables);
|
||||
|
||||
$autoId = true;
|
||||
if (isset($this->params['disable-autoid'])) {
|
||||
$autoId = !$this->params['disable-autoid'];
|
||||
}
|
||||
|
||||
return [
|
||||
'plugin' => $this->plugin,
|
||||
'pluginPath' => $pluginPath,
|
||||
'namespace' => $namespace,
|
||||
'collection' => $collection,
|
||||
'tables' => $tables,
|
||||
'action' => 'create_table',
|
||||
'name' => $this->BakeTemplate->viewVars['name'],
|
||||
'autoId' => $autoId,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a collection from a database
|
||||
*
|
||||
* @param string $connection Database connection name.
|
||||
* @return \Cake\Database\Schema\Collection
|
||||
*/
|
||||
public function getCollection($connection)
|
||||
{
|
||||
$connection = ConnectionManager::get($connection);
|
||||
|
||||
return $connection->getSchemaCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* To check if a Table Model is to be added in the migration file
|
||||
*
|
||||
* @param string $tableName Table name in underscore case.
|
||||
* @param string|null $pluginName Plugin name if exists.
|
||||
* @deprecated Will be removed in the next version
|
||||
* @return bool True if the model is to be added.
|
||||
*/
|
||||
public function tableToAdd($tableName, $pluginName = null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
|
||||
$parser->setDescription(
|
||||
'Bake migration snapshot class.'
|
||||
)->addArgument('name', [
|
||||
'help' => 'Name of the migration to bake. Can use Plugin.name to bake migration files into plugins.',
|
||||
'required' => true,
|
||||
])
|
||||
->addOption('require-table', [
|
||||
'boolean' => true,
|
||||
'default' => false,
|
||||
'help' => 'If require-table is set to true, check also that the table class exists.',
|
||||
])->addOption('disable-autoid', [
|
||||
'boolean' => true,
|
||||
'default' => false,
|
||||
'help' => 'Disable phinx behavior of automatically adding an id field.',
|
||||
])
|
||||
->addOption('no-lock', [
|
||||
'help' => 'If present, no lock file will be generated after baking',
|
||||
'boolean' => true,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Event\Event;
|
||||
use Cake\Event\EventManager;
|
||||
use Cake\Utility\Inflector;
|
||||
use Migrations\Util\ColumnParser;
|
||||
|
||||
/**
|
||||
* Task class for generating migration snapshot files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class MigrationTask extends SimpleMigrationTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
EventManager::instance()->on('Bake.initialize', function (Event $event) {
|
||||
$event->getSubject()->loadHelper('Migrations.Migration');
|
||||
});
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Migrations.config/skeleton';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function templateData()
|
||||
{
|
||||
$className = $this->BakeTemplate->viewVars['name'];
|
||||
$namespace = Configure::read('App.namespace');
|
||||
$pluginPath = '';
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->_pluginNamespace($this->plugin);
|
||||
$pluginPath = $this->plugin . '.';
|
||||
}
|
||||
|
||||
$action = $this->detectAction($className);
|
||||
|
||||
if (empty($action)) {
|
||||
return [
|
||||
'plugin' => $this->plugin,
|
||||
'pluginPath' => $pluginPath,
|
||||
'namespace' => $namespace,
|
||||
'tables' => [],
|
||||
'action' => null,
|
||||
'name' => $className,
|
||||
];
|
||||
}
|
||||
|
||||
$arguments = $this->args;
|
||||
unset($arguments[0]);
|
||||
$columnParser = new ColumnParser();
|
||||
$fields = $columnParser->parseFields($arguments);
|
||||
$indexes = $columnParser->parseIndexes($arguments);
|
||||
$primaryKey = $columnParser->parsePrimaryKey($arguments);
|
||||
|
||||
if (in_array($action[0], ['alter_table', 'add_field']) && !empty($primaryKey)) {
|
||||
$this->abort('Adding a primary key to an already existing table is not supported.');
|
||||
}
|
||||
|
||||
list($action, $table) = $action;
|
||||
|
||||
return [
|
||||
'plugin' => $this->plugin,
|
||||
'pluginPath' => $pluginPath,
|
||||
'namespace' => $namespace,
|
||||
'tables' => [$table],
|
||||
'action' => $action,
|
||||
'columns' => [
|
||||
'fields' => $fields,
|
||||
'indexes' => $indexes,
|
||||
'primaryKey' => $primaryKey,
|
||||
],
|
||||
'name' => $className,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects the action and table from the name of a migration
|
||||
*
|
||||
* @param string $name Name of migration
|
||||
* @return array
|
||||
**/
|
||||
public function detectAction($name)
|
||||
{
|
||||
if (preg_match('/^(Create|Drop)(.*)/', $name, $matches)) {
|
||||
$action = strtolower($matches[1]) . '_table';
|
||||
$table = Inflector::underscore($matches[2]);
|
||||
} elseif (preg_match('/^(Add).+?(?:To)(.*)/', $name, $matches)) {
|
||||
$action = 'add_field';
|
||||
$table = Inflector::underscore($matches[2]);
|
||||
} elseif (preg_match('/^(Remove).+?(?:From)(.*)/', $name, $matches)) {
|
||||
$action = 'drop_field';
|
||||
$table = Inflector::underscore($matches[2]);
|
||||
} elseif (preg_match('/^(Alter).+?(?:On)(.*)/', $name, $matches)) {
|
||||
$action = 'alter_field';
|
||||
$table = Inflector::underscore($matches[2]);
|
||||
} elseif (preg_match('/^(Alter)(.*)/', $name, $matches)) {
|
||||
$action = 'alter_table';
|
||||
$table = Inflector::underscore($matches[2]);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [$action, $table];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
/**
|
||||
* This task class is needed in order to provide a correct autocompletion feature
|
||||
* when using the CakePHP migrations shell plugin. It has no effect on the
|
||||
* migrations process.
|
||||
*/
|
||||
class RollbackTask extends CommandTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser
|
||||
->addOption('target', [
|
||||
'short' => 't',
|
||||
'help' => 'The version number to migrate to',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('date', [
|
||||
'short' => 'd',
|
||||
'help' => 'The date to migrate to',
|
||||
'required' => false,
|
||||
])
|
||||
->addOption('dry-run', ['short' => 'x'])
|
||||
->addOption('fake', ['boolean' => true])
|
||||
->addOption('no-lock', [
|
||||
'help' => 'If present, no lock file will be generated after rolling back',
|
||||
'boolean' => true,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Bake\Shell\Task\SimpleBakeTask;
|
||||
use Cake\Console\ConsoleOptionParser;
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Plugin as CorePlugin;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Task class for generating seed files.
|
||||
*
|
||||
* @property \Bake\Shell\Task\BakeTemplateTask $BakeTemplate
|
||||
* @property \Bake\Shell\Task\TestTask $Test
|
||||
*/
|
||||
class SeedTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* path to Migration directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'config/Seeds/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'seed';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
return Inflector::camelize($name) . 'Seed.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
$path = ROOT . DS . $this->pathFragment;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . $this->pathFragment;
|
||||
}
|
||||
|
||||
return str_replace('/', DS, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function template()
|
||||
{
|
||||
return 'Migrations.Seed/seed';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function templateData()
|
||||
{
|
||||
$namespace = Configure::read('App.namespace');
|
||||
if ($this->plugin) {
|
||||
$namespace = $this->_pluginNamespace($this->plugin);
|
||||
}
|
||||
|
||||
$table = Inflector::tableize($this->args[0]);
|
||||
if (!empty($this->params['table'])) {
|
||||
$table = $this->params['table'];
|
||||
}
|
||||
|
||||
$records = false;
|
||||
if ($this->param('data')) {
|
||||
$limit = (int)$this->param('limit');
|
||||
|
||||
$fields = $this->param('fields') ?: '*';
|
||||
if ($fields !== '*') {
|
||||
$fields = explode(',', $fields);
|
||||
}
|
||||
|
||||
$connection = ConnectionManager::get($this->connection);
|
||||
|
||||
$query = $connection->newQuery()
|
||||
->from($table)
|
||||
->select($fields);
|
||||
|
||||
if ($limit) {
|
||||
$query->limit($limit);
|
||||
}
|
||||
|
||||
$records = $connection->execute($query)->fetchAll('assoc');
|
||||
$records = $this->prettifyArray($records);
|
||||
}
|
||||
|
||||
return [
|
||||
'className' => $this->BakeTemplate->viewVars['name'],
|
||||
'namespace' => $namespace,
|
||||
'records' => $records,
|
||||
'table' => $table,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$this->params['no-test'] = true;
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$name = ($this->plugin ? $this->plugin . '.' : '') . $this->name;
|
||||
$parser = new ConsoleOptionParser($name);
|
||||
|
||||
$bakeThemes = [];
|
||||
foreach (CorePlugin::loaded() as $plugin) {
|
||||
$path = CorePlugin::classPath($plugin);
|
||||
if (is_dir($path . 'Template' . DS . 'Bake')) {
|
||||
$bakeThemes[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
$parser->setDescription(
|
||||
'Bake seed class.'
|
||||
)->addOption('plugin', [
|
||||
'short' => 'p',
|
||||
'help' => 'Plugin to bake into.',
|
||||
])->addOption('force', [
|
||||
'short' => 'f',
|
||||
'boolean' => true,
|
||||
'help' => 'Force overwriting existing files without prompting.',
|
||||
])->addOption('connection', [
|
||||
'short' => 'c',
|
||||
'default' => 'default',
|
||||
'help' => 'The datasource connection to get data from.',
|
||||
])->addOption('table', [
|
||||
'help' => 'The database table to use.',
|
||||
])->addOption('theme', [
|
||||
'short' => 't',
|
||||
'help' => 'The theme to use when baking code.',
|
||||
'choices' => $bakeThemes,
|
||||
])->addArgument('name', [
|
||||
'help' => 'Name of the seed to bake. Can use Plugin.name to bake plugin models.',
|
||||
])->addOption('data', [
|
||||
'boolean' => true,
|
||||
'help' => 'Include data from the table to the seed',
|
||||
])->addOption('fields', [
|
||||
'default' => '*',
|
||||
'help' => 'If including data, comma separated list of fields to select (all fields by default)',
|
||||
])->addOption('limit', [
|
||||
'short' => 'l',
|
||||
'help' => 'If including data, max number of rows to select',
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prettify var_export of an array output
|
||||
*
|
||||
* @param array $array Array to prettify
|
||||
* @param int $tabCount Initial tab count
|
||||
* @param string $indentCharacter Desired indent for the code.
|
||||
* @return string
|
||||
*/
|
||||
protected function prettifyArray($array, $tabCount = 3, $indentCharacter = " ")
|
||||
{
|
||||
$content = var_export($array, true);
|
||||
|
||||
$lines = explode("\n", $content);
|
||||
|
||||
$inString = false;
|
||||
|
||||
foreach ($lines as $k => &$line) {
|
||||
if ($k === 0) {
|
||||
// First row
|
||||
$line = '[';
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($k === count($lines) - 1) {
|
||||
// Last row
|
||||
$line = str_repeat($indentCharacter, --$tabCount) . ']';
|
||||
continue;
|
||||
}
|
||||
|
||||
$line = ltrim($line);
|
||||
|
||||
if (!$inString) {
|
||||
if ($line === '),') {
|
||||
//Check for closing bracket
|
||||
$line = '],';
|
||||
$tabCount--;
|
||||
} elseif (preg_match("/^\d+\s\=\>\s$/", $line)) {
|
||||
// Mark '0 =>' kind of lines to remove
|
||||
$line = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Insert tab count
|
||||
$line = str_repeat($indentCharacter, $tabCount) . $line;
|
||||
}
|
||||
|
||||
$length = strlen($line);
|
||||
for ($j = 0; $j < $length; $j++) {
|
||||
if ($line[$j] === '\\') {
|
||||
//skip character right after an escape \
|
||||
$j++;
|
||||
} elseif ($line[$j] === '\'') {
|
||||
//check string open/end
|
||||
$inString = !$inString;
|
||||
}
|
||||
}
|
||||
|
||||
//check for opening bracket
|
||||
if (!$inString && trim($line) === 'array (') {
|
||||
$line = str_replace('array (', '[', $line);
|
||||
$tabCount++;
|
||||
}
|
||||
}
|
||||
unset($line);
|
||||
|
||||
// Remove marked lines
|
||||
$lines = array_filter($lines, function ($line) {
|
||||
return $line !== false;
|
||||
});
|
||||
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Bake\Shell\Task\SimpleBakeTask;
|
||||
use Cake\Console\ConsoleOptionParser;
|
||||
use Cake\Core\Plugin as CorePlugin;
|
||||
use Cake\Utility\Inflector;
|
||||
use Phinx\Util\Util;
|
||||
|
||||
/**
|
||||
* Task class for generating migration snapshot files.
|
||||
*/
|
||||
abstract class SimpleMigrationTask extends SimpleBakeTask
|
||||
{
|
||||
/**
|
||||
* path to Migration directory
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $pathFragment = 'config/Migrations/';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function name()
|
||||
{
|
||||
return 'migration';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function fileName($name)
|
||||
{
|
||||
$name = $this->getMigrationName($name);
|
||||
|
||||
return Util::getCurrentTimestamp() . '_' . Inflector::camelize($name) . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
$path = ROOT . DS . $this->pathFragment;
|
||||
if (isset($this->plugin)) {
|
||||
$path = $this->_pluginPath($this->plugin) . $this->pathFragment;
|
||||
}
|
||||
|
||||
return str_replace('/', DS, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function bake($name)
|
||||
{
|
||||
$migrationWithSameName = glob($this->getPath() . '*_' . $name . '.php');
|
||||
if (!empty($migrationWithSameName)) {
|
||||
$force = $this->param('force');
|
||||
if (!$force) {
|
||||
$this->abort(
|
||||
sprintf(
|
||||
'A migration with the name `%s` already exists. Please use a different name.',
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->info(sprintf('A migration with the name `%s` already exists, it will be deleted.', $name));
|
||||
foreach ($migrationWithSameName as $migration) {
|
||||
$this->info(sprintf('Deleting migration file `%s`...', $migration));
|
||||
if (unlink($migration)) {
|
||||
$this->success(sprintf('Deleted `%s`', $migration));
|
||||
} else {
|
||||
$this->err(sprintf('An error occurred while deleting `%s`', $migration));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->params['no-test'] = true;
|
||||
|
||||
return parent::bake($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a class name for the migration class
|
||||
*
|
||||
* If the name is invalid, the task will exit
|
||||
*
|
||||
* @param string|null $name Name for the generated migration
|
||||
* @return string|null Name of the migration file or null if empty
|
||||
*/
|
||||
protected function getMigrationName($name = null)
|
||||
{
|
||||
if (empty($name)) {
|
||||
$this->abort('Choose a migration name to bake in CamelCase format');
|
||||
}
|
||||
|
||||
$name = $this->_getName($name);
|
||||
$name = Inflector::camelize($name);
|
||||
|
||||
if (!preg_match('/^[A-Z]{1}[a-zA-Z0-9]+$/', $name)) {
|
||||
$this->abort('The className is not correct. The className can only contain "A-Z" and "0-9".');
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the option parser instance and configures it.
|
||||
*
|
||||
* @return \Cake\Console\ConsoleOptionParser
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$name = ($this->plugin ? $this->plugin . '.' : '') . $this->name;
|
||||
$parser = new ConsoleOptionParser($name);
|
||||
|
||||
$bakeThemes = [];
|
||||
foreach (CorePlugin::loaded() as $plugin) {
|
||||
$path = CorePlugin::classPath($plugin);
|
||||
if (is_dir($path . 'Template' . DS . 'Bake')) {
|
||||
$bakeThemes[] = $plugin;
|
||||
}
|
||||
}
|
||||
|
||||
$parser->setDescription(
|
||||
'Bake migration class.'
|
||||
)
|
||||
->addOption('plugin', [
|
||||
'short' => 'p',
|
||||
'help' => 'Plugin to bake into.',
|
||||
])
|
||||
->addOption('force', [
|
||||
'short' => 'f',
|
||||
'boolean' => true,
|
||||
'help' => 'Force overwriting existing file if a migration already exists with the same name.',
|
||||
])
|
||||
->addOption('connection', [
|
||||
'short' => 'c',
|
||||
'default' => 'default',
|
||||
'help' => 'The datasource connection to get data from.',
|
||||
])
|
||||
->addOption('theme', [
|
||||
'short' => 't',
|
||||
'help' => 'The theme to use when baking code.',
|
||||
'choices' => $bakeThemes,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
use Cake\Core\App;
|
||||
use Cake\Core\Plugin;
|
||||
use Cake\Database\Schema\Collection;
|
||||
use Cake\Datasource\ConnectionManager;
|
||||
use Cake\Filesystem\Folder;
|
||||
use Cake\ORM\TableRegistry;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Trait needed for all "snapshot" type of bake operations.
|
||||
* Snapshot type operations are : baking a snapshot and baking a diff.
|
||||
*/
|
||||
trait SnapshotTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* After a file has been successfully created, we mark the newly
|
||||
* created migration as applied
|
||||
*
|
||||
* @param string $path Where to put the file.
|
||||
* @param string $contents Content to put in the file.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function createFile($path, $contents)
|
||||
{
|
||||
$createFile = parent::createFile($path, $contents);
|
||||
|
||||
if ($createFile) {
|
||||
$this->markSnapshotApplied($path);
|
||||
|
||||
if (!isset($this->params['no-lock']) || !$this->params['no-lock']) {
|
||||
$this->refreshDump();
|
||||
}
|
||||
}
|
||||
|
||||
return $createFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will mark a snapshot created, the snapshot being identified by its
|
||||
* full file path.
|
||||
*
|
||||
* @param string $path Path to the newly created snapshot
|
||||
* @return void
|
||||
*/
|
||||
protected function markSnapshotApplied($path)
|
||||
{
|
||||
$fileName = pathinfo($path, PATHINFO_FILENAME);
|
||||
list($version, ) = explode('_', $fileName, 2);
|
||||
|
||||
$dispatchCommand = 'migrations mark_migrated -t ' . $version . ' -o';
|
||||
if (!empty($this->params['connection'])) {
|
||||
$dispatchCommand .= ' -c ' . $this->params['connection'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$dispatchCommand .= ' -p ' . $this->params['plugin'];
|
||||
}
|
||||
|
||||
$this->_io->out('Marking the migration ' . $fileName . ' as migrated...');
|
||||
$this->dispatchShell($dispatchCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* After a file has been successfully created, we refresh the dump of the database
|
||||
* to be able to generate a new diff afterward.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function refreshDump()
|
||||
{
|
||||
$dispatchCommand = 'migrations dump';
|
||||
if (!empty($this->params['connection'])) {
|
||||
$dispatchCommand .= ' -c ' . $this->params['connection'];
|
||||
}
|
||||
|
||||
if (!empty($this->params['plugin'])) {
|
||||
$dispatchCommand .= ' -p ' . $this->params['plugin'];
|
||||
}
|
||||
|
||||
$this->_io->out('Creating a dump of the new database state...');
|
||||
$this->dispatchShell($dispatchCommand);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
||||
* @link http://cakephp.org CakePHP(tm) Project
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Migrations\Shell\Task;
|
||||
|
||||
/**
|
||||
* This task class is needed in order to provide a correct autocompletion feature
|
||||
* when using the CakePHP migrations shell plugin. It has no effect on the
|
||||
* migrations process.
|
||||
*/
|
||||
class StatusTask extends CommandTask
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getOptionParser()
|
||||
{
|
||||
$parser = parent::getOptionParser();
|
||||
$parser->addOption('format', [
|
||||
'short' => 'f',
|
||||
'help' => 'The output format: text or json. Defaults to text.',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user