Use anonymous classes to avoid the duplicated class name for database migrations. (#6839)

Co-authored-by: 10470 <xiexuekun@addcn.com>
Co-authored-by: zds <49744633+zds-s@users.noreply.github.com>
This commit is contained in:
rookiexxk 2024-08-16 15:52:37 +08:00 committed by GitHub
parent 3558e6fab9
commit a37af75d6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 157 additions and 26 deletions

View File

@ -6,6 +6,7 @@ TRAVIS_BUILD_DIR="${TRAVIS_BUILD_DIR:-$(dirname $(dirname $CURRENT_DIR))}"
echo -e "Create MySQL database..."
mysql -h 127.0.0.1 -u root -e "CREATE DATABASE IF NOT EXISTS hyperf charset=utf8mb4 collate=utf8mb4_unicode_ci;"
mysql -h 127.0.0.1 -u root -e "CREATE DATABASE IF NOT EXISTS hyperf2 charset=utf8mb4 collate=utf8mb4_unicode_ci;"
mysql -h 127.0.0.1 -u root -e "CREATE DATABASE IF NOT EXISTS hyperf3 charset=utf8mb4 collate=utf8mb4_unicode_ci;"
cat "${TRAVIS_BUILD_DIR}/.travis/hyperf.sql" | mysql -h 127.0.0.1 -u root hyperf
echo -e "Done\n"

View File

@ -1,5 +1,9 @@
# v3.1.37 - TBD
## Optimized
- [#6839](https://github.com/hyperf/hyperf/pull/6839) Use `anonymous classes` to avoid the duplicated class name for database migrations.
# v3.1.36 - 2024-08-15
## Added

View File

@ -52,7 +52,7 @@ class MigrationCreator
$this->files->put(
$path = $this->getPath($name, $path),
$this->populateStub($name, $stub, $table)
$this->populateStub($stub, $table)
);
// Next, we will fire any hooks that are supposed to fire after a migration is
@ -127,10 +127,8 @@ class MigrationCreator
/**
* Populate the place-holders in the migration stub.
*/
protected function populateStub(string $name, string $stub, ?string $table): string
protected function populateStub(string $stub, ?string $table): string
{
$stub = str_replace('DummyClass', $this->getClassName($name), $stub);
// Here we will replace the table place-holders with the table specified by
// the developer, which is useful for quickly creating a tables creation
// or update migration from the console instead of typing it manually.

View File

@ -19,6 +19,7 @@ use Hyperf\Database\ConnectionResolverInterface as Resolver;
use Hyperf\Database\Schema\Grammars\Grammar;
use Hyperf\Stringable\Str;
use Hyperf\Support\Filesystem\Filesystem;
use ReflectionClass;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;
@ -164,7 +165,7 @@ class Migrator
*/
public function resolve(string $file): object
{
$class = Str::studly(implode('_', array_slice(explode('_', $file), 4)));
$class = $this->getMigrationClass($file);
return new $class();
}
@ -283,6 +284,27 @@ class Migrator
return $this;
}
/**
* Resolve a migration instance from migration path.
*/
protected function resolvePath(string $path): object
{
$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.
*/
protected function getMigrationClass(string $migrationName): string
{
return Str::studly(implode('_', array_slice(explode('_', $migrationName), 4)));
}
/**
* Get the migration files that have not yet run.
*/
@ -304,9 +326,8 @@ class Migrator
// 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) {
$this->pretendToRun($migration, 'up');
@ -402,9 +423,8 @@ class Migrator
// 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("<comment>Rolling back:</comment> {$name}");
@ -459,6 +479,11 @@ class Migrator
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("<info>{$name}:</info> {$query['query']}");
}
}

View File

@ -4,7 +4,7 @@ use Hyperf\Database\Schema\Schema;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Migrations\Migration;
class DummyClass extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@ -21,4 +21,4 @@ class DummyClass extends Migration
{
//
}
}
};

View File

@ -4,7 +4,7 @@ use Hyperf\Database\Schema\Schema;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Migrations\Migration;
class DummyClass extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@ -24,4 +24,4 @@ class DummyClass extends Migration
{
Schema::dropIfExists('DummyTable');
}
}
};

View File

@ -4,7 +4,7 @@ use Hyperf\Database\Schema\Schema;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Migrations\Migration;
class DummyClass extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@ -25,4 +25,4 @@ class DummyClass extends Migration
//
});
}
}
};

View File

@ -45,8 +45,8 @@ class DatabaseMigrationCreatorTest extends TestCase
$creator = $this->getCreator();
$creator->expects($this->any())->method('getDatePrefix')->will($this->returnValue('foo'));
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/blank.stub')->andReturn('DummyClass');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar');
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/blank.stub')->andReturn('return new class');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class');
$creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']);
$creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php');
@ -64,8 +64,8 @@ class DatabaseMigrationCreatorTest extends TestCase
});
$creator->expects($this->any())->method('getDatePrefix')->will($this->returnValue('foo'));
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/update.stub')->andReturn('DummyClass DummyTable');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz');
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/update.stub')->andReturn('return new class DummyTable');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class baz');
$creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']);
$creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php');
@ -80,8 +80,8 @@ class DatabaseMigrationCreatorTest extends TestCase
{
$creator = $this->getCreator();
$creator->expects($this->any())->method('getDatePrefix')->will($this->returnValue('foo'));
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/update.stub')->andReturn('DummyClass DummyTable');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz');
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/update.stub')->andReturn('return new class DummyTable');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class baz');
$creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']);
$creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php');
@ -92,8 +92,8 @@ class DatabaseMigrationCreatorTest extends TestCase
{
$creator = $this->getCreator();
$creator->expects($this->any())->method('getDatePrefix')->will($this->returnValue('foo'));
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/create.stub')->andReturn('DummyClass DummyTable');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'CreateBar baz');
$creator->getFilesystem()->shouldReceive('get')->once()->with($creator->stubPath() . '/create.stub')->andReturn('return new class DummyTable');
$creator->getFilesystem()->shouldReceive('put')->once()->with('foo/foo_create_bar.php', 'return new class baz');
$creator->getFilesystem()->shouldReceive('glob')->once()->with('foo/*.php')->andReturn(['foo/foo_create_bar.php']);
$creator->getFilesystem()->shouldReceive('requireOnce')->once()->with('foo/foo_create_bar.php');

View File

@ -60,8 +60,10 @@ class DatabaseMigratorIntegrationTest extends TestCase
];
$connection = $connector->make($dbConfig);
$connection2 = $connector->make(array_merge($dbConfig, ['database' => 'hyperf2']));
$connection3 = $connector->make(array_merge($dbConfig, ['database' => 'hyperf3']));
$resolver = new ConnectionResolver(['default' => $connection]);
$resolver = new ConnectionResolver(['default' => $connection, 'mysql2' => $connection2, 'mysql3' => $connection3]);
$container->shouldReceive('get')->with(ConnectionResolverInterface::class)->andReturn($resolver);
@ -299,6 +301,23 @@ class DatabaseMigratorIntegrationTest extends TestCase
$builder->drop('test_change_types');
}
public function testMigrationsCanEachDefineConnection()
{
$schema = new Schema();
$ran = $this->migrator->run([__DIR__ . '/migrations/connection_configured']);
$this->assertFalse($schema->hasTable('failed_jobs'));
$this->assertFalse($schema->hasTable('jobs'));
$this->assertFalse($schema->connection('mysql2')->getSchemaBuilder()->hasTable('failed_jobs'));
$this->assertFalse($schema->connection('mysql2')->getSchemaBuilder()->hasTable('jobs'));
$this->assertTrue($schema->connection('mysql3')->getSchemaBuilder()->hasTable('failed_jobs'));
$this->assertTrue($schema->connection('mysql3')->getSchemaBuilder()->hasTable('jobs'));
$this->migrator->rollback([__DIR__ . '/migrations/connection_configured']);
$this->assertTrue(Str::contains($ran[0], 'failed_jobs'));
$this->assertTrue(Str::contains($ran[1], 'jobs'));
}
protected function getConnection(): Connection
{
return $this->migrator->resolveConnection('default');

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Database\Migrations\Migration;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Schema\Schema;
return new class extends Migration {
/**
* The database connection that should be used by the migration.
*/
protected string $connection = 'mysql3';
/**
* Run the migrations.
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down()
{
Schema::dropIfExists('failed_jobs');
}
};

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Database\Migrations\Migration;
use Hyperf\Database\Schema\Blueprint;
use Hyperf\Database\Schema\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up()
{
(new Schema())->connection('mysql3')->getSchemaBuilder()->create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}
/**
* Reverse the migrations.
*/
public function down()
{
(new Schema())->connection('mysql3')->getSchemaBuilder()->dropIfExists('jobs');
}
};