diff --git a/src/Illuminate/Database/Migrations/Migrator.php b/src/Illuminate/Database/Migrations/Migrator.php
index 093e41e78a43..b51db2a142c5 100755
--- a/src/Illuminate/Database/Migrations/Migrator.php
+++ b/src/Illuminate/Database/Migrations/Migrator.php
@@ -13,6 +13,7 @@
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
+use ReflectionClass;
use Symfony\Component\Console\Output\OutputInterface;
class Migrator
@@ -185,9 +186,8 @@ protected function runUp($file, $batch, $pretend)
// First we will resolve a "real" instance of the migration class from this
// migration file name. Once we have the instances we can run the actual
// command such as "up" or "down", or we can just simulate the action.
- $migration = $this->resolve(
- $name = $this->getMigrationName($file)
- );
+ $migration = $this->resolvePath($file);
+ $name = $this->getMigrationName($file);
if ($pretend) {
return $this->pretendToRun($migration, 'up');
@@ -348,9 +348,8 @@ protected function runDown($file, $migration, $pretend)
// First we will get the file name of the migration so we can resolve out an
// instance of the migration. Once we get an instance we can either run a
// pretend execution of the migration or we can run the real migration.
- $instance = $this->resolve(
- $name = $this->getMigrationName($file)
- );
+ $instance = $this->resolvePath($file);
+ $name = $this->getMigrationName($file);
$this->note("Rolling back: {$name}");
@@ -413,6 +412,11 @@ protected function pretendToRun($migration, $method)
foreach ($this->getQueries($migration, $method) as $query) {
$name = get_class($migration);
+ $reflectionClass = new ReflectionClass($migration);
+ if ($reflectionClass->isAnonymous()) {
+ $name = $this->getMigrationName($reflectionClass->getFileName());
+ }
+
$this->note("{$name}: {$query['query']}");
}
}
@@ -448,11 +452,38 @@ protected function getQueries($migration, $method)
*/
public function resolve($file)
{
- $class = Str::studly(implode('_', array_slice(explode('_', $file), 4)));
+ $class = $this->getMigrationClass($file);
return new $class;
}
+ /**
+ * Resolve a migration instance from migration path.
+ *
+ * @param string $path
+ * @return object
+ */
+ protected function resolvePath(string $path)
+ {
+ $class = $this->getMigrationClass($this->getMigrationName($path));
+ if (class_exists($class)) {
+ return new $class;
+ }
+
+ return $this->files->getRequire($path);
+ }
+
+ /**
+ * Generate migration class name based on migration name.
+ *
+ * @param string $migrationName
+ * @return string
+ */
+ protected function getMigrationClass(string $migrationName): string
+ {
+ return Str::studly(implode('_', array_slice(explode('_', $migrationName), 4)));
+ }
+
/**
* Get all of the migration files in a given path.
*
diff --git a/tests/Integration/Migration/MigratorTest.php b/tests/Integration/Migration/MigratorTest.php
index 50ee6f3cbd6b..08dd9c97862f 100644
--- a/tests/Integration/Migration/MigratorTest.php
+++ b/tests/Integration/Migration/MigratorTest.php
@@ -2,11 +2,29 @@
namespace Illuminate\Tests\Integration\Migration;
+use Illuminate\Support\Facades\DB;
+use Mockery;
+use Mockery\Mock;
use Orchestra\Testbench\TestCase;
-use PDOException;
+use Symfony\Component\Console\Output\OutputInterface;
class MigratorTest extends TestCase
{
+ /**
+ * @var Mock
+ */
+ private $output;
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->output = Mockery::mock(OutputInterface::class);
+ $this->subject = $this->app->make('migrator');
+ $this->subject->setOutput($this->output);
+ $this->subject->getRepository()->createRepository();
+ }
+
protected function getEnvironmentSetUp($app)
{
$app['config']->set('app.debug', 'true');
@@ -19,25 +37,55 @@ protected function getEnvironmentSetUp($app)
]);
}
- public function testDontDisplayOutputWhenOutputObjectIsNotAvailable()
+ public function testMigrate()
+ {
+ $this->expectOutput('Migrating: 2014_10_12_000000_create_people_table');
+ $this->expectOutput(Mockery::pattern('#Migrated: 2014_10_12_000000_create_people_table (.*)#'));
+ $this->expectOutput('Migrating: 2015_10_04_000000_modify_people_table');
+ $this->expectOutput(Mockery::pattern('#Migrated: 2015_10_04_000000_modify_people_table (.*)#'));
+ $this->expectOutput('Migrating: 2016_10_04_000000_modify_people_table');
+ $this->expectOutput(Mockery::pattern('#Migrated: 2016_10_04_000000_modify_people_table (.*)#'));
+
+ $this->subject->run([__DIR__.'/fixtures']);
+
+ self::assertTrue(DB::getSchemaBuilder()->hasTable('people'));
+ self::assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'first_name'));
+ self::assertTrue(DB::getSchemaBuilder()->hasColumn('people', 'last_name'));
+ }
+
+ public function testRollback()
{
- $migrator = $this->app->make('migrator');
+ $this->getConnection()->statement('CREATE TABLE people(id INT, first_name VARCHAR, last_name VARCHAR);');
+ $this->subject->getRepository()->log('2014_10_12_000000_create_people_table', 1);
+ $this->subject->getRepository()->log('2015_10_04_000000_modify_people_table', 1);
+ $this->subject->getRepository()->log('2016_10_04_000000_modify_people_table', 1);
- $migrator->getRepository()->createRepository();
+ $this->expectOutput('Rolling back: 2016_10_04_000000_modify_people_table');
+ $this->expectOutput(Mockery::pattern('#Rolled back: 2016_10_04_000000_modify_people_table (.*)#'));
+ $this->expectOutput('Rolling back: 2015_10_04_000000_modify_people_table');
+ $this->expectOutput(Mockery::pattern('#Rolled back: 2015_10_04_000000_modify_people_table (.*)#'));
+ $this->expectOutput('Rolling back: 2014_10_12_000000_create_people_table');
+ $this->expectOutput(Mockery::pattern('#Rolled back: 2014_10_12_000000_create_people_table (.*)#'));
- $migrator->run([__DIR__.'/fixtures']);
+ $this->subject->rollback([__DIR__.'/fixtures']);
- $this->assertTrue($this->tableExists('people'));
+ self::assertFalse(DB::getSchemaBuilder()->hasTable('people'));
}
- private function tableExists($table): bool
+ public function testPretendMigrate()
{
- try {
- $this->app->make('db')->select("SELECT COUNT(*) FROM $table");
- } catch (PDOException $e) {
- return false;
- }
+ $this->expectOutput('CreatePeopleTable: create table "people" ("id" integer not null primary key autoincrement, "name" varchar not null, "email" varchar not null, "password" varchar not null, "remember_token" varchar, "created_at" datetime, "updated_at" datetime)');
+ $this->expectOutput('CreatePeopleTable: create unique index "people_email_unique" on "people" ("email")');
+ $this->expectOutput('2015_10_04_000000_modify_people_table: alter table "people" add column "first_name" varchar');
+ $this->expectOutput('2016_10_04_000000_modify_people_table: alter table "people" add column "last_name" varchar');
+
+ $this->subject->run([__DIR__.'/fixtures'], ['pretend' => true]);
- return true;
+ self::assertFalse(DB::getSchemaBuilder()->hasTable('people'));
+ }
+
+ private function expectOutput($argument): void
+ {
+ $this->output->shouldReceive('writeln')->once()->with($argument);
}
}
diff --git a/tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php b/tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php
new file mode 100644
index 000000000000..88ac706cd12a
--- /dev/null
+++ b/tests/Integration/Migration/fixtures/2015_10_04_000000_modify_people_table.php
@@ -0,0 +1,31 @@
+string('first_name')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('people', function (Blueprint $table) {
+ $table->dropColumn('first_name');
+ });
+ }
+};
diff --git a/tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php b/tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php
new file mode 100644
index 000000000000..6492b6d7f55a
--- /dev/null
+++ b/tests/Integration/Migration/fixtures/2016_10_04_000000_modify_people_table.php
@@ -0,0 +1,31 @@
+string('last_name')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('people', function (Blueprint $table) {
+ $table->dropColumn('last_name');
+ });
+ }
+};