Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Release Notes for Craft CMS 6

## Unreleased

- Fixed a bug where Yii-style migrations could be required twice. ([#19376](https://github.com/craftcms/cms/pull/19376))

## 6.0.0-alpha.15 - 2026-08-04

- Added support for Markdown-based custom Dashboard widgets in the application's `resources/widgets/` directory. ([#19319](https://github.com/craftcms/cms/pull/19319))
Expand Down
36 changes: 36 additions & 0 deletions yii2-adapter/src/Database/Migrator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace CraftCms\Yii2Adapter\Database;

use CraftCms\Cms\Database\Migration;
use CraftCms\Cms\Database\Migrator as CoreMigrator;
use Override;
use ReflectionClass;

class Migrator extends CoreMigrator
{
#[Override]
protected function resolvePath(string $path): object
{
$migrationName = $this->getMigrationName($path);
$realPath = realpath($path);

foreach (array_reverse(get_declared_classes()) as $class) {
if ($class !== $migrationName && !str_ends_with($class, "\\$migrationName")) {
continue;
}

if ($realPath !== new ReflectionClass($class)->getFileName()) {
continue;
}

return is_a($class, Migration::class, true)
? app()->make($class)
: new MigrationWrapper($class);
}

return parent::resolvePath($path);
}
}
10 changes: 10 additions & 0 deletions yii2-adapter/src/Yii2ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use CraftCms\Cms\Cms;
use CraftCms\Cms\Cp\Settings;
use CraftCms\Cms\Database\LaravelMigrations;
use CraftCms\Cms\Database\MigrationRepository;
use CraftCms\Cms\Database\Migrator as CoreMigrator;
use CraftCms\Cms\Database\Table;
use CraftCms\Cms\Field\Events\FieldCachesInvalidated;
use CraftCms\Cms\Gql\Gql;
Expand Down Expand Up @@ -42,6 +44,7 @@
use CraftCms\Yii2Adapter\Console\MigrateSessionsTableCommand;
use CraftCms\Yii2Adapter\Console\RepairCategoryGroupStructureCommand;
use CraftCms\Yii2Adapter\Cp\LegacySettings;
use CraftCms\Yii2Adapter\Database\Migrator;
use CraftCms\Yii2Adapter\Filesystem\FilesystemCompatibility;
use CraftCms\Yii2Adapter\Gql\LegacyGql;
use CraftCms\Yii2Adapter\Gql\LegacyGqlArguments;
Expand All @@ -62,6 +65,7 @@
use CraftCms\Yii2Adapter\Utility\LegacyUtilityTypes;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Http\Kernel as HttpKernel;
use Illuminate\Database\Migrations\MigrationRepositoryInterface;
use Illuminate\Foundation\Exceptions\Handler;
use Illuminate\Routing\Router;
use Illuminate\Support\Facades\Artisan;
Expand All @@ -84,6 +88,12 @@ class Yii2ServiceProvider extends ServiceProvider
#[Override]
public function register(): void
{
$this->app->bind(CoreMigrator::class, Migrator::class);
$this->app
->when(Migrator::class)
->needs(MigrationRepositoryInterface::class)
->give(fn() => $this->app->make(MigrationRepository::class, ['table' => Table::MIGRATIONS]));

new ClassAliases()->register();
new MultiEnvironmentConfigCompatibility()->register($this->app);

Expand Down
43 changes: 43 additions & 0 deletions yii2-adapter/tests-laravel/Legacy/Database/MigratorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

use CraftCms\Cms\Database\MigrationRepository;
use CraftCms\Cms\Database\Migrator as CoreMigrator;
use CraftCms\Yii2Adapter\Database\MigrationWrapper;
use CraftCms\Yii2Adapter\Database\Migrator;
use Illuminate\Database\Migrations\Migrator as IlluminateMigrator;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\File;

afterEach(function() {
File::delete(storage_path('m260226_120000_product_type_permissions.php'));
});

it('resolves a loaded Yii-style migration without requiring it again', function() {
$path = storage_path('m260226_120000_product_type_permissions.php');
File::put($path, <<<'PHP'
<?php

namespace CraftCms\Yii2Adapter\Tests\Fixtures;

class m260226_120000_product_type_permissions {}
PHP);

$filesystem = Mockery::mock(Filesystem::class)->makePartial();
$filesystem->shouldReceive('getRequire')->andReturn(new stdClass());

$migrator = app(CoreMigrator::class);
new ReflectionProperty(IlluminateMigrator::class, 'files')->setValue($migrator, $filesystem);

$migrator->requireFiles([$path]);

$resolvePath = new ReflectionMethod(Migrator::class, 'resolvePath');
$migration = $resolvePath->invoke($migrator, $path);

$filesystem->shouldNotHaveReceived('getRequire');

expect($migrator)->toBeInstanceOf(Migrator::class);
expect($migrator->getRepository())->toBeInstanceOf(MigrationRepository::class);
expect($migration)->toBeInstanceOf(MigrationWrapper::class);
});
Loading