对于仍然对此感兴趣的任何人,Laravel 5 更新: Laravel 已经实现了一次运行一个迁移文件的选项(在 5.7 版中)。
你现在可以运行这个:
php artisan migrate --path=/database/migrations/my_migration.php (回复here)
因为Illuminate\Database\Migrations\Migrator::getMigrationFiles() 现在包含以下代码:
return Str::endsWith($path, '.php') ? [$path] : $this->files->glob($path.'/*_*.php');
(见the source code。)
但在我的用例中,我实际上想同时运行一组迁移,而不仅仅是一个或全部。
所以我采用 Laravel 的方式,注册了一个不同的 Migrator 实现,它决定使用哪些文件:
/**
* A migrator that can run multiple specifically chosen migrations.
*/
class MigrationsSetEnabledMigrator extends Migrator
{
/**
* @param Migrator $migrator
*/
public function __construct(Migrator $migrator)
{
parent::__construct($migrator->repository, $migrator->resolver, $migrator->files);
// Compatibility with versions >= 5.8
if (isset($migrator->events)) {
$this->events = $migrator->events;
}
}
/**
* Get all of the migration files in a given path.
*
* @param string|array $paths
* @return array
*/
public function getMigrationFiles($paths)
{
return Collection::make($paths)->flatMap(function ($path) {
return Str::endsWith($path, ']') ? $this->parseArrayOfPaths($path) :
(Str::endsWith($path, '.php') ? [$path] : $this->files->glob($path . '/*_*.php'));
})->filter()->sortBy(function ($file) {
return $this->getMigrationName($file);
})->values()->keyBy(function ($file) {
return $this->getMigrationName($file);
})->all();
}
public function parseArrayOfPaths($path)
{
$prefix = explode('[', $path)[0];
$filePaths = explode('[', $path)[1];
$filePaths = rtrim($filePaths, ']');
return Collection::make(explode(',', $filePaths))->map(function ($filePath) use ($prefix) {
return $prefix . $filePath;
})->all();
}
}
我们必须将它作为'migrator' 注册到容器中(以$app['migrator'] 访问),因为这是在将自身注册到 IoC 时 Migrate 命令访问它的方式。为此,我们将这段代码放入服务提供者中(在我的例子中,它是DatabaseServiceProvider):
public function register()
{
$this->app->extend('migrator', function ($migrator, $app) {
return new MultipleSpecificMigrationsEnabledMigrator($migrator);
});
// We reset the command.migrate bind, which uses the migrator - to
// force refresh of the migrator instance.
$this->app->instance('command.migrate', null);
}
然后你可以运行这个:
php artisan migrate --path=[database/migrations/my_migration.php,database/migrations/another_migration.php]
注意多个迁移文件,以逗号分隔。
它在 Laravel 5.4 中经过测试和工作,应该与 Laravel 5.8 兼容。
为什么?
对于任何感兴趣的人:用例是更新数据库版本及其数据。
例如,假设您想将所有用户的街道和门牌号码合并到新列中,我们称之为street_and_house。想象一下,您想以安全且经过测试的方式在多个安装中执行此操作 - 您可能会为此创建一个脚本(在我的情况下,我创建数据版本控制命令 - 工匠命令)。
要进行这样的操作,您首先必须将用户加载到内存中;然后运行迁移以删除旧列并添加新列;然后为每个用户分配street_and_house=$street . " " . $house_no 并保存用户。 (我这里是在简化,但你肯定可以想象其他场景)
而且我不想依赖我可以在任何给定时间运行所有迁移的事实。想象一下,您想将它从 1.0.0 更新到 1.2.0,并且有多批这样的更新 - 执行任何更多的迁移可能会破坏您的数据,因为这些迁移必须由它们自己的专用更新命令处理。因此,我只想运行此更新知道如何使用的选定已知迁移,然后对数据执行操作,然后可能运行下一个更新数据命令。 (我想尽可能地防守)。
为了实现这一点,我需要上述机制并定义一组固定的迁移以运行这样的命令。
注意:我宁愿使用一个简单的装饰器,利用神奇的 __call 方法并避免继承(Laravel 在 \Illuminate\Database\Eloquent\Builder 中使用的类似机制来包装 \Illuminate\Database\Query\Builder),但是 @遗憾的是,987654338@ 在其构造函数中需要Migrator 的实例。
最后注:我想将这个答案发布到问题 How can I run specific migration in laravel ,因为它是 Laravel 5 特定的。但我不能——因为这个问题被标记为这个问题的副本(尽管这个问题被标记为 Laravel 4)。