/var/www/vhosts/ihelp.ro/_OLD/vendor/cakephp/migrations/src/Command
Edit: /var/www/vhosts/ihelp.ro/_OLD/vendor/cakephp/migrations/src/Command/BakeMigrationDiffCommand.php (20486B)
setup($args);
if (!$this->checkSync()) {
$io->abort('Your migrations history is not in sync with your migrations files. ' .
'Make sure all your migrations have been migrated before baking a diff.');
}
if (empty($this->migrationsFiles) && empty($this->migratedItems)) {
$this->bakeSnapshot($name, $args, $io);
}
$collection = $this->getCollection($this->connection);
EventManager::instance()->on('Bake.initialize', function (Event $event) use ($collection) {
$event->getSubject()->loadHelper('Migrations.Migration', [
'collection' => $collection,
]);
});
parent::bake($name, $args, $io);
}
/**
* Sets up everything the baking process needs
*
* @param \Cake\Console\Arguments $args The command arguments.
* @return void
*/
protected function setup(Arguments $args)
{
$this->migrationsPath = $this->getPath($args);
$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, true);
$migratedItems = [];
if ($tableExists) {
$query = $connection->newQuery();
/** @var array $migratedItems */
$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();
}
/**
* @inheritDoc
*/
public function templateData(Arguments $arguments): array
{
$this->dumpSchema = $this->getDumpSchema($arguments);
$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\TableSchema 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);
/** @var int $key */
$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($column['fixed']);
unset($oldColumn['collate']);
unset($oldColumn['fixed']);
if (
in_array($columnName, $oldColumns, true) &&
$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);
/** @var int $key */
$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'] === TableSchema::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, true) &&
$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'] === TableSchema::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, true) &&
$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.
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return int|null Value of the snapshot baking dispatch process
*/
protected function bakeSnapshot($name, Arguments $args, ConsoleIo $io)
{
$io->out('Your migrations history is empty and you do not have any migrations files.');
$io->out('Falling back to baking a snapshot...');
$newArgs = [];
$newArgs[] = $name;
if ($args->getOption('connection')) {
$newArgs[] = '-c';
$newArgs[] = $args->getOption('connection');
}
if ($args->getOption('plugin')) {
$newArgs[] = '-p';
$newArgs[] = $args->getOption('plugin');
}
$exitCode = $this->executeCommand(BakeMigrationSnapshotCommand::class, $newArgs, $io);
if ($exitCode === 1) {
$io->abort('Something went wrong during the snapshot baking. Please try again.');
}
return $exitCode;
}
/**
* Fetch the correct schema dump based on the arguments and options passed to the shell call
* and returns it as an array
*
* @param \Cake\Console\Arguments $args The command arguments.
* @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(Arguments $args)
{
$inputArgs = [];
$connectionName = 'default';
if (!empty($args->getOption('connection'))) {
$connectionName = $inputArgs['--connection'] = $args->getOption('connection');
}
if (!empty($args->getOption('plugin'))) {
$inputArgs['--plugin'] = $args->getOption('plugin');
}
$className = Dump::class;
$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->io->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;
}
$connection = ConnectionManager::get($this->connection);
if (method_exists($connection, 'cacheMetadata')) {
$connection->cacheMetadata(false);
}
$collection = $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(): string
{
return 'Migrations.config/diff';
}
/**
* Gets the option parser instance and configures it.
*
* @return \Cake\Console\ConsoleOptionParser
*/
public function getOptionParser(): ConsoleOptionParser
{
$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;
}
}