public function handle(InputInterface $input, OutputInterface $output) { $registry = BaseOrm::getRegistry(); $loader = new Loader(); $issues = $loader->detectConflicts(); if (!empty($issues)) { $message = 'The following migrations seem to indicate they are both the latest migration :' . PHP_EOL; $message .= ' %s ' . PHP_EOL; $output->writeln(sprintf($message, Tools::stringify($issues))); return; } if ($input->getOption('no-interaction')) { $asker = NonInteractiveAsker::createObject($input, $output); } else { $asker = InteractiveAsker::createObject($input, $output); } $autodetector = new AutoDetector($loader->getProjectState(), ProjectState::fromApps($registry), $asker); $changes = $autodetector->getChanges($loader->graph); if (empty($changes)) { $output->writeln('No changes were detected'); return; } if ($input->getOption('dry-run')) { $output->writeln('<info>Migrations :</info>'); /** @var $migration Migration */ foreach ($changes as $migration) { $output->writeln(' -- ' . $migration->getName()); } return; } $this->_writeMigrations($changes, $input, $output); }
/** * Does the actual alteration of the model table. * * @param SchemaEditor $schemaEditor * @param ProjectState $fromState * @param ProjectState $toState * * @since 1.1.0 * * @author Eddilbert Macharia (http://eddmash.com) <*****@*****.**> */ private function _alterModelTable($schemaEditor, $fromState, $toState) { $toModel = $toState->getRegistry()->getModel($this->name); if ($this->allowMigrateModel($schemaEditor->connection, $toModel)) { $fromModel = $fromState->getRegistry()->getModel($this->name); $schemaEditor->alterDbTable($toModel, $fromModel->meta->dbTable, $toModel->meta->dbTable); // Rename M2M fields whose name is based on this model's db_table /** @var $newField ManyToManyField */ /* @var $oldField ManyToManyField */ foreach ($toModel->meta->localManyToMany as $newName => $newField) { foreach ($fromModel->meta->localManyToMany as $oldName => $oldField) { if ($newName === $oldName) { $schemaEditor->alterDbTable($newField->relation->through, $oldField->relation->through->meta->dbTable, $newField->relation->through->meta->dbTable); } } } } }
public function handle(InputInterface $input, OutputInterface $output) { $name = $input->getArgument('migration_name'); if ($input->getOption('fake')) { $fake = true; } else { $fake = false; } $connection = BaseOrm::getDbConnection(); $registry = BaseOrm::getRegistry(); $executor = Executor::createObject($connection); // target migrations to act on if (!empty($name)) { if ($name == 'zero') { $targets = [$name]; } else { $targets = $executor->loader->getMigrationByPrefix($name); } } else { $targets = $executor->loader->graph->getLeafNodes(); } // get migration plan $plan = $executor->getMigrationPlan($targets); BaseOrm::signalDispatch('powerorm.migration.pre_migrate', $this); $output->writeln('<comment>Running migrations:</comment>'); if (empty($plan)) { $output->writeln(' No migrations to apply.'); if ($input->getOption('no-interaction')) { $asker = NonInteractiveAsker::createObject($input, $output); } else { $asker = InteractiveAsker::createObject($input, $output); } //detect if we need to make migrations $auto_detector = new AutoDetector($executor->loader->getProjectState(), ProjectState::fromApps($registry), $asker); $changes = $auto_detector->getChanges($executor->loader->graph); if (!empty($changes)) { $output->writeln('<warning> Your models have changes that are not yet reflected ' . "in a migration, and so won't be applied.</warning>"); $output->writeln("<warning> Run 'php pmanager.php makemigrations' to make new " . "migrations, and then re-run 'php pmanager.php migrate' to apply them.</warning>"); } } else { // migrate $executor->migrate($targets, $plan, $fake); } BaseOrm::signalDispatch('powerorm.migration.post_migrate', $this); }
/** * Takes a ProjectState and returns a new one with the migration's operations applied to it. * * Preserves the original object state by default and will return a mutated state from a copy. * * @param ProjectState $state * @param bool|true $preserveState * * @return mixed * * @since 1.1.0 * * @author Eddilbert Macharia (http://eddmash.com) <*****@*****.**> */ public function updateState($state, $preserveState = true) { $newState = $state; if ($preserveState) { $newState = $state->deepClone(); } /** @var $operation Operation */ foreach ($this->operations as $operation) { $operation->updateState($newState); } return $newState; }
/** * Create ProjectState based on migrations on disk. * * @return ProjectState * * @throws NodeNotFoundError * * @since 1.1.0 * * @author Eddilbert Macharia (http://eddmash.com) <*****@*****.**> */ public function getState() { $leaves = $this->getLeafNodes(); $state = ProjectState::createObject(); if (empty($leaves)) { return $state; } // from the leave go up its family tree though its parents and ancestors until we get to the root_node. // this way we get the full lineage we need to follow to get to this leaf from root_node to leaf_node // we use this lineage to apply migrations in database $lineage = []; foreach ($leaves as $leaf) { // get lineage $lineage_members = $this->getAncestryTree($leaf); foreach ($lineage_members as $i => $l_member) { if (in_array($l_member, $lineage)) { continue; } $lineage[] = $l_member; } } // use the lineage to update the project state based on the migrations. /* @var $migration Migration */ foreach ($lineage as $member) { $migration = $this->nodes[$member]; $state = $migration->updateState($state); } return $state; }
/** * Migrates the database up to the given targets. * * @param $targets * @param $plan * @param $fake * * @since 1.1.0 * * @author Eddilbert Macharia (http://eddmash.com) <*****@*****.**> */ public function migrate($targets, $plan, $fake) { if (empty($plan)) { $plan = $this->getMigrationPlan($targets); } $migrationsToRun = $this->getMigrationsFromPlan($plan); // the full plan that would be executed if we to run on a new database $fullPlan = $this->getMigrationsFromPlan($this->getMigrationPlan($this->loader->graph->getLeafNodes(), true)); // Holds all states right before a migration is applied // if the migration is being run. $states = []; $state = ProjectState::createObject(); //Phase 1 -- create all project states before a migration is (un)applied /** @var $migration Migration */ foreach ($fullPlan as $migName => $migration) { // we use the migration to mutate state // after we mutate we remove the migration from the $migrationsToRun list. // so if we get to a point where we dont have any more $migrationsToRun break // this is to avoid any further mutations by other migrations not in the list. if (empty($migrationsToRun)) { break; } $run = ArrayHelper::hasKey($migrationsToRun, $migName); if ($run) { $states[$migName] = $state->deepClone(); unset($migrationsToRun[$migName]); } // $run will be false if the migration is not in the $migrationsToRun list // so there is not need to preserve state else if its in the list we need to we will get a new state object // that has been altered by the migration. // we do this because we need the object stored in the states array in the condition it was right before // the migration was applied. // remember in PHP objects are passed by reference. $state = $migration->updateState($state, $run); } // Phase 2 -- Run the migrations foreach ($plan as $mName => $migrationMeta) { if ($migrationMeta['unapply']) { $this->unApplyMigration($states[$mName], $migrationMeta['migration'], $fake); } else { $this->applyMigration($states[$mName], $migrationMeta['migration'], $fake); } } }
/** * Does the actual field alteration. * * @param SchemaEditor $schemaEditor * @param ProjectState $fromState * @param ProjectState $toState * * @since 1.1.0 * * @author Eddilbert Macharia (http://eddmash.com) <*****@*****.**> */ private function _alterField($schemaEditor, $fromState, $toState) { $toModel = $toState->getRegistry()->getModel($this->modelName); if ($this->allowMigrateModel($schemaEditor->connection, $toModel)) { $fromModel = $fromState->getRegistry()->getModel($this->modelName); $fromField = $fromModel->meta->getField($this->name); $toField = $toModel->meta->getField($this->name); if (false === $this->preserveDefault) { $toField->default = $this->field->default; } $schemaEditor->alterField($fromModel, $fromField, $toField); if (false === $this->preserveDefault) { $toField->default = NOT_PROVIDED; } } }