diff --git a/.travis/setup.mysql.sh b/.travis/setup.mysql.sh index e837c116b..b66581fc3 100755 --- a/.travis/setup.mysql.sh +++ b/.travis/setup.mysql.sh @@ -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" diff --git a/CHANGELOG-3.1.md b/CHANGELOG-3.1.md index 4bd226d1c..6e178def3 100644 --- a/CHANGELOG-3.1.md +++ b/CHANGELOG-3.1.md @@ -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 diff --git a/src/database/src/Migrations/MigrationCreator.php b/src/database/src/Migrations/MigrationCreator.php index e01fc7b53..f0ff6a098 100755 --- a/src/database/src/Migrations/MigrationCreator.php +++ b/src/database/src/Migrations/MigrationCreator.php @@ -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. diff --git a/src/database/src/Migrations/Migrator.php b/src/database/src/Migrations/Migrator.php index 98b5c4729..aa4f68690 100755 --- a/src/database/src/Migrations/Migrator.php +++ b/src/database/src/Migrations/Migrator.php @@ -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("Rolling back: {$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("{$name}: {$query['query']}"); } } diff --git a/src/database/src/Migrations/stubs/blank.stub b/src/database/src/Migrations/stubs/blank.stub index 40b600922..4d2f36eca 100755 --- a/src/database/src/Migrations/stubs/blank.stub +++ b/src/database/src/Migrations/stubs/blank.stub @@ -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 { // } -} +}; diff --git a/src/database/src/Migrations/stubs/create.stub b/src/database/src/Migrations/stubs/create.stub index b00c4f513..1c5301146 100755 --- a/src/database/src/Migrations/stubs/create.stub +++ b/src/database/src/Migrations/stubs/create.stub @@ -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'); } -} +}; diff --git a/src/database/src/Migrations/stubs/update.stub b/src/database/src/Migrations/stubs/update.stub index 62acd1da5..af8ba1262 100755 --- a/src/database/src/Migrations/stubs/update.stub +++ b/src/database/src/Migrations/stubs/update.stub @@ -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 // }); } -} +}; diff --git a/src/database/tests/DatabaseMigrationCreatorTest.php b/src/database/tests/DatabaseMigrationCreatorTest.php index c117ac6f0..f07a0374b 100755 --- a/src/database/tests/DatabaseMigrationCreatorTest.php +++ b/src/database/tests/DatabaseMigrationCreatorTest.php @@ -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'); diff --git a/src/database/tests/DatabaseMigratorIntegrationTest.php b/src/database/tests/DatabaseMigratorIntegrationTest.php index e0c4fed66..0e7397bff 100644 --- a/src/database/tests/DatabaseMigratorIntegrationTest.php +++ b/src/database/tests/DatabaseMigratorIntegrationTest.php @@ -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'); diff --git a/src/database/tests/migrations/connection_configured/2024_06_12_000000_create_failed_jobs_table.php b/src/database/tests/migrations/connection_configured/2024_06_12_000000_create_failed_jobs_table.php new file mode 100644 index 000000000..fa7001314 --- /dev/null +++ b/src/database/tests/migrations/connection_configured/2024_06_12_000000_create_failed_jobs_table.php @@ -0,0 +1,44 @@ +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'); + } +}; diff --git a/src/database/tests/migrations/connection_configured/2024_06_12_120000_create_jobs_table.php b/src/database/tests/migrations/connection_configured/2024_06_12_120000_create_jobs_table.php new file mode 100644 index 000000000..43c7872be --- /dev/null +++ b/src/database/tests/migrations/connection_configured/2024_06_12_120000_create_jobs_table.php @@ -0,0 +1,40 @@ +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'); + } +};