/** * 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); } } }