mirror of
https://github.com/flarum/core.git
synced 2025-10-19 10:46:06 +02:00
301 lines
8.0 KiB
PHP
301 lines
8.0 KiB
PHP
<?php
|
|
|
|
/*
|
|
* This file is part of Flarum.
|
|
*
|
|
* For detailed copyright and license information, please view the
|
|
* LICENSE file that was distributed with this source code.
|
|
*/
|
|
|
|
namespace Flarum\Database;
|
|
|
|
use Exception;
|
|
use Flarum\Extension\Extension;
|
|
use Illuminate\Database\ConnectionInterface;
|
|
use Illuminate\Database\Schema\Builder;
|
|
use Illuminate\Filesystem\Filesystem;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
class Migrator
|
|
{
|
|
/**
|
|
* The migration repository implementation.
|
|
*
|
|
* @var \Flarum\Database\MigrationRepositoryInterface
|
|
*/
|
|
protected $repository;
|
|
|
|
/**
|
|
* The filesystem instance.
|
|
*
|
|
* @var \Illuminate\Filesystem\Filesystem
|
|
*/
|
|
protected $files;
|
|
|
|
/**
|
|
* The database schema builder instance.
|
|
*
|
|
* @var Builder
|
|
*/
|
|
protected $schemaBuilder;
|
|
|
|
/**
|
|
* The output interface implementation.
|
|
*
|
|
* @var OutputInterface
|
|
*/
|
|
protected $output;
|
|
|
|
/**
|
|
* Create a new migrator instance.
|
|
*
|
|
* @param MigrationRepositoryInterface $repository
|
|
* @param ConnectionInterface $connection
|
|
* @param Filesystem $files
|
|
*/
|
|
public function __construct(
|
|
MigrationRepositoryInterface $repository,
|
|
ConnectionInterface $connection,
|
|
Filesystem $files
|
|
) {
|
|
$this->files = $files;
|
|
$this->repository = $repository;
|
|
|
|
$this->schemaBuilder = $connection->getSchemaBuilder();
|
|
|
|
// Workaround for https://github.com/laravel/framework/issues/1186
|
|
$connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');
|
|
}
|
|
|
|
/**
|
|
* Run the outstanding migrations at a given path.
|
|
*
|
|
* @param string $path
|
|
* @param Extension $extension
|
|
* @return void
|
|
*/
|
|
public function run($path, Extension $extension = null)
|
|
{
|
|
$files = $this->getMigrationFiles($path);
|
|
|
|
$ran = $this->repository->getRan($extension ? $extension->getId() : null);
|
|
|
|
$migrations = array_diff($files, $ran);
|
|
|
|
$this->runMigrationList($path, $migrations, $extension);
|
|
}
|
|
|
|
/**
|
|
* Run an array of migrations.
|
|
*
|
|
* @param string $path
|
|
* @param array $migrations
|
|
* @param Extension $extension
|
|
* @return void
|
|
*/
|
|
public function runMigrationList($path, $migrations, Extension $extension = null)
|
|
{
|
|
// First we will just make sure that there are any migrations to run. If there
|
|
// aren't, we will just make a note of it to the developer so they're aware
|
|
// that all of the migrations have been run against this database system.
|
|
if (count($migrations) == 0) {
|
|
$this->note('<info>Nothing to migrate.</info>');
|
|
|
|
return;
|
|
}
|
|
|
|
// Once we have the array of migrations, we will spin through them and run the
|
|
// migrations "up" so the changes are made to the databases. We'll then log
|
|
// that the migration was run so we don't repeat it next time we execute.
|
|
foreach ($migrations as $file) {
|
|
$this->runUp($path, $file, $extension);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run "up" a migration instance.
|
|
*
|
|
* @param string $path
|
|
* @param string $file
|
|
* @param string $path
|
|
* @param Extension $extension
|
|
* @return void
|
|
*/
|
|
protected function runUp($path, $file, Extension $extension = null)
|
|
{
|
|
$migration = $this->resolve($path, $file);
|
|
|
|
$this->runClosureMigration($migration);
|
|
|
|
// Once we have run a migrations class, we will log that it was run in this
|
|
// repository so that we don't try to run it next time we do a migration
|
|
// in the application. A migration repository keeps the migrate order.
|
|
$this->repository->log($file, $extension ? $extension->getId() : null);
|
|
|
|
$this->note("<info>Migrated:</info> $file");
|
|
}
|
|
|
|
/**
|
|
* Rolls all of the currently applied migrations back.
|
|
*
|
|
* @param string $path
|
|
* @param Extension $extension
|
|
* @return int
|
|
*/
|
|
public function reset($path, Extension $extension = null)
|
|
{
|
|
$migrations = array_reverse($this->repository->getRan(
|
|
$extension ? $extension->getId() : null
|
|
));
|
|
|
|
$count = count($migrations);
|
|
|
|
if ($count === 0) {
|
|
$this->note('<info>Nothing to rollback.</info>');
|
|
} else {
|
|
foreach ($migrations as $migration) {
|
|
$this->runDown($path, $migration, $extension);
|
|
}
|
|
}
|
|
|
|
return $count;
|
|
}
|
|
|
|
/**
|
|
* Run "down" a migration instance.
|
|
*
|
|
* @param string $path
|
|
* @param string $file
|
|
* @param string $path
|
|
* @param Extension $extension
|
|
* @return void
|
|
*/
|
|
protected function runDown($path, $file, Extension $extension = null)
|
|
{
|
|
$migration = $this->resolve($path, $file);
|
|
|
|
$this->runClosureMigration($migration, 'down');
|
|
|
|
// Once we have successfully run the migration "down" we will remove it from
|
|
// the migration repository so it will be considered to have not been run
|
|
// by the application then will be able to fire by any later operation.
|
|
$this->repository->delete($file, $extension ? $extension->getId() : null);
|
|
|
|
$this->note("<info>Rolled back:</info> $file");
|
|
}
|
|
|
|
/**
|
|
* Runs a closure migration based on the migrate direction.
|
|
*
|
|
* @param $migration
|
|
* @param string $direction
|
|
* @throws Exception
|
|
*/
|
|
protected function runClosureMigration($migration, $direction = 'up')
|
|
{
|
|
if (is_array($migration) && array_key_exists($direction, $migration)) {
|
|
call_user_func($migration[$direction], $this->schemaBuilder);
|
|
} else {
|
|
throw new Exception('Migration file should contain an array with up/down.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all of the migration files in a given path.
|
|
*
|
|
* @param string $path
|
|
* @return array
|
|
*/
|
|
public function getMigrationFiles($path)
|
|
{
|
|
$files = $this->files->glob($path.'/*_*.php');
|
|
|
|
if ($files === false) {
|
|
return [];
|
|
}
|
|
|
|
$files = array_map(function ($file) {
|
|
return str_replace('.php', '', basename($file));
|
|
}, $files);
|
|
|
|
// Once we have all of the formatted file names we will sort them and since
|
|
// they all start with a timestamp this should give us the migrations in
|
|
// the order they were actually created by the application developers.
|
|
sort($files);
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Resolve a migration instance from a file.
|
|
*
|
|
* @param string $path
|
|
* @param string $file
|
|
* @return array
|
|
*/
|
|
public function resolve($path, $file)
|
|
{
|
|
$migration = "$path/$file.php";
|
|
|
|
if ($this->files->exists($migration)) {
|
|
return $this->files->getRequire($migration);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the output implementation that should be used by the console.
|
|
*
|
|
* @param OutputInterface $output
|
|
* @return $this
|
|
*/
|
|
public function setOutput(OutputInterface $output)
|
|
{
|
|
$this->output = $output;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Write a note to the conosle's output.
|
|
*
|
|
* @param string $message
|
|
* @return void
|
|
*/
|
|
protected function note($message)
|
|
{
|
|
if ($this->output) {
|
|
$this->output->writeln($message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the migration repository instance.
|
|
*
|
|
* @return MigrationRepositoryInterface
|
|
*/
|
|
public function getRepository()
|
|
{
|
|
return $this->repository;
|
|
}
|
|
|
|
/**
|
|
* Determine if the migration repository exists.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function repositoryExists()
|
|
{
|
|
return $this->repository->repositoryExists();
|
|
}
|
|
|
|
/**
|
|
* Get the file system instance.
|
|
*
|
|
* @return \Illuminate\Filesystem\Filesystem
|
|
*/
|
|
public function getFilesystem()
|
|
{
|
|
return $this->files;
|
|
}
|
|
}
|