如何干预Laravel默认的迁移执行顺序?
我现在正在用Laravel开发一个面向插件的CMS,但在尝试干预Laravel的默认迁移顺序时遇到了瓶颈。Laravel默认会按文件名的时间戳前缀来执行迁移,但在我的CMS场景中,迁移顺序始终是 core -> addons -> app,到目前为止还没找到这个问题的解决办法。
我知道可以把所有文件直接发布到应用端,并根据它们所属的分组来修改它们的文件名时间戳,但我更愿意不把任何核心逻辑暴露给应用端。
我想做的事:
干预任何依赖迁移顺序的Artisan调用(例如migration:run、migration:down等),并改变默认的文件排序逻辑。
到目前为止我尝试过:
我创建了一个Migrator文件,它扩展了Laravel的默认Migrator。
<?php
namespace CMS\Database\Migrators;
use Illuminate\Database\Migrations\Migrator as LaravelMigrator;
class Migrator extends LaravelMigrator
{
public function run($paths = [], array $options = []): array
{
dd("debug"); // never fires on any db related artisan call
$this->runCore();
$this->runAddons();
return [];
}
}
然后我在一个服务提供者的register方法里把它设置为单例(这一点在任何数据库相关的服务提供者之前,这应该没有关系)
<?php
namespace CMS\Providers;
use Illuminate\Database\Migrations\MigrationRepositoryInterface;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Migrations\Migrator as LaravelMigrator;
use CMS\Database\Migrators\Migrator as CMSMigrator;
class DatabaseServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(LaravelMigrator::class, function ($app) {
return new CMSMigrator(
$app['files'],
$app['db'],
$app['events'],
$app->make(MigrationRepositoryInterface::class)
);
});
}
}
软件信息
{
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1"
}
谢谢。
解决方案
不要把它做成单例,而是扩展默认的Migrator实例
<?php
namespace CMS\Providers;
use Illuminate\Support\ServiceProvider;
use CMS\Database\Migrators\Migrator as CMSMigrator;
use CMS\Helpers\Package;
class DatabaseServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->extend('migrator', function ($_migrator, $app) {
return new CMSMigrator(
$app['migration.repository'],
$app['db'],
$app['files'],
$app['events']
);
});
}
}
用如下构造函数对它进行预加载
<?php
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Migrations\Migrator as LaravelMigrator;
use Illuminate\Events\Dispatcher;
use Illuminate\Filesystem\Filesystem;
class Migrator extends LaravelMigrator
{
public function __construct()
{
$repository = app('migration.repository');
$resolver = app(DatabaseManager::class);
$files = app(Filesystem::class);
$dispatcher = app(Dispatcher::class);
parent::__construct($repository, $resolver, $files, $dispatcher);
}
}
最后添加一个run方法,参数是字符串数组,返回一个数组
<?php
public function run($paths = [], array $options = []): array
{
$corePath = "my-package-migrations-path";
$addons = [["name" => "a1", "migrationPath" => "path1"], ["name" => "a2", "migrationPath" => "path2"]];
parent::run([$corePath]);
parent::run(array_map(fn ($addon) => $addon['migrationPath'], $addons));
// Prevent default Laravel from running paths again, I am not sure with this one just yet
return [];
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。