src/SnooPHP/Model/Migration.php
protected function migrate()
	{
		// Program status
		$status = true;

		// Compute table dependencies
		// break if circular dependencies are found
		if (($newTables = static::computeDependencies($this->tables)) === false)
		{
			error_log("\e[1;31m!\e[0m circular dependency found; terminating program\n");
			return false;
		}

		// Retrieve last migration
		$migration	= $this->lastMigration();
		$oldTables	= $migration ? unserialize($migration["tables"]) : [];

		// Begin transaction
		// Unfortunately begin transaction has no effect here
		// MySQL adds an implicit commit after DROP TABLE and CREATE TABLE
		printf("\n> starting migration for \e[1;37m'{$this->dbName}'\e[0m database ...\n");
		Db::beginTransaction($this->dbName);

		// Check new tables
		foreach ($newTables as $newTable)
		{
			if (!$newTable->active) continue;
			
			// Find matching table
			$oldTable = null;
			foreach ($oldTables as $oldTab)
				if ($oldTab->name() === $newTable->name())
				{
					$oldTable = $oldTab;
					break;
				}

			// Run migration
			try
			{
				$oldTable ?
				printf("\n> processing existing table \e[1;37m'{$newTable->name()}'\e[0m:\n") :
				printf("\n> creating new table \e[1;37m'{$newTable->name()}'\e[0m:\n{$newTable->createQuery()}\n");
				$newTable->migrate($oldTable, $this->dbName);
				printf("\e[1;32m✓\e[0m all ok\n");
			}
			catch (PDOException $e)
			{
				error_log("\e[1;31m!\e[0m {$e->getMessage()}\n");
				$status = false;
			}
		}

		// Check old tables
		$dropped = $oldTables;
		foreach ($dropped as $i => $t)
			foreach ($newTables as $newTable)
				if ($newTable->name() === $t->name())
				{
					unset($dropped[$i]);
					break;
				}

		if (!empty($dropped))
		{
			// Get table names
			printf("\n> dropping tables:\n");
			foreach ($dropped as $i => $t)
			{
				printf("    ➤ {$t->name()}\n");
				$dropped[$i] = $t->name();
			}

			// Drop tables
			try
			{
				Db::query("drop table if exists ".implode(", ", $dropped), [], $this->dbName, false);
			}
			catch (PDOException $e)
			{
				error_log("\n\e[1;31m!\e[0m {$e->getMessage()}\n");
				$status = false;
			}
		}
		

		if ($status)
		{
			// All ok
			// Create migration table if it does not exist
			$this->createMigrationTable();
			
			// Save migration
			$status = $this->saveMigration($newTables);
			if ($status)
			{
				printf("\n\e[1;32m✓\e[0m migration saved\n");
				Db::commit($this->dbName);
			}
			else
			{

				error_log("\n\e[1;31m!\e[0m error while saving migration; reverting changes ...\n");
				Db::rollBack($this->dbName);
			}
		}
		else
		{
			// Revert changes to database
			// policy is "all or nothing"
			Db::rollBack($this->dbName);
		}

		// Return program status
		return $status;
	}
protected function migrate()
	{
		// Program status
		$status = true;

		// Compute table dependencies
		// break if circular dependencies are found
		if (($newTables = static::computeDependencies($this->tables)) === false)
		{
			error_log("\e[1;31m!\e[0m circular dependency found; terminating program\n");
			return false;
		}

		// Retrieve last migration
		$migration	= $this->lastMigration();
		$oldTables	= $migration ? unserialize($migration["tables"]) : [];

		// Begin transaction
		// Unfortunately begin transaction has no effect here
		// MySQL adds an implicit commit after DROP TABLE and CREATE TABLE
		printf("\n> starting migration for \e[1;37m'{$this->dbName}'\e[0m database ...\n");
		Db::beginTransaction($this->dbName);

		// Check new tables
		foreach ($newTables as $newTable)
		{
			if (!$newTable->active) continue;
			
			// Find matching table
			$oldTable = null;
			foreach ($oldTables as $oldTab)
				if ($oldTab->name() === $newTable->name())
				{
					$oldTable = $oldTab;
					break;
				}

			// Run migration
			try
			{
				$oldTable ?
				printf("\n> processing existing table \e[1;37m'{$newTable->name()}'\e[0m:\n") :
				printf("\n> creating new table \e[1;37m'{$newTable->name()}'\e[0m:\n{$newTable->createQuery()}\n");
				$newTable->migrate($oldTable, $this->dbName);
				printf("\e[1;32m✓\e[0m all ok\n");
			}
			catch (PDOException $e)
			{
				error_log("\e[1;31m!\e[0m {$e->getMessage()}\n");
				$status = false;
			}
		}

		// Check old tables
		$dropped = $oldTables;
		foreach ($dropped as $i => $t)
			foreach ($newTables as $newTable)
				if ($newTable->name() === $t->name())
				{
					unset($dropped[$i]);
					break;
				}

		if (!empty($dropped))
		{
			// Get table names
			printf("\n> dropping tables:\n");
			foreach ($dropped as $i => $t)
			{
				printf("    ➤ {$t->name()}\n");
				$dropped[$i] = $t->name();
			}

			// Drop tables
			try
			{
				Db::query("drop table if exists ".implode(", ", $dropped), [], $this->dbName, false);
			}
			catch (PDOException $e)
			{
				error_log("\n\e[1;31m!\e[0m {$e->getMessage()}\n");
				$status = false;
			}
		}
		

		if ($status)
		{
			// All ok
			// Create migration table if it does not exist
			$this->createMigrationTable();
			
			// Save migration
			$status = $this->saveMigration($newTables);
			if ($status)
			{
				printf("\n\e[1;32m✓\e[0m migration saved\n");
				Db::commit($this->dbName);
			}
			else
			{

				error_log("\n\e[1;31m!\e[0m error while saving migration; reverting changes ...\n");
				Db::rollBack($this->dbName);
			}
		}
		else
		{
			// Revert changes to database
			// policy is "all or nothing"
			Db::rollBack($this->dbName);
		}

		// Return program status
		return $status;
	}
Run migration

@return bool program status