如何让php artisan migrate动态执行原生SQL脚本集合?
接入原生SQL脚本到Laravel
migrate 命令的方案 不用把每个SQL脚本转成PHP迁移文件,你可以通过扩展Laravel的Migrator类实现动态加载并执行SQL脚本,同时利用Laravel自带的迁移追踪机制记录执行状态。以下是可行的实现步骤:
1. 创建自定义的SQL迁移处理器
新建app/Migration/SqlMigrator.php,继承Laravel原生Migrator,重写迁移文件收集和执行逻辑:
<?php namespace App\Migration; use Illuminate\Database\Migrations\Migrator as BaseMigrator; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; class SqlMigrator extends BaseMigrator { // SQL脚本存放目录,可按需修改 protected $sqlMigrationPath; public function __construct($repository, $resolver, $files) { parent::__construct($repository, $resolver, $files); $this->sqlMigrationPath = database_path('sql-migrations'); } // 同时收集PHP迁移文件和SQL脚本 public function getMigrationFiles($paths) { // 获取原生PHP迁移文件 $phpMigrations = parent::getMigrationFiles($paths); // 收集指定目录下的所有SQL脚本 $sqlMigrations = $this->files->glob($this->sqlMigrationPath . '/*.sql'); // 合并后按时间戳排序(和Laravel迁移排序逻辑一致) $allMigrations = array_merge($phpMigrations, $sqlMigrations); usort($allMigrations, function ($a, $b) { return strcmp($this->getMigrationName($a), $this->getMigrationName($b)); }); return $allMigrations; } // 处理SQL脚本的执行逻辑 protected function runMigration($file, $method) { if (Str::endsWith($file, '.sql')) { $migrationName = $this->getMigrationName($file); // 跳过已执行过的SQL脚本 if ($this->repository->exists($migrationName)) { return; } // 读取并执行SQL脚本 $sqlContent = $this->files->get($file); DB::unprepared($sqlContent); // 记录到migrations表,标记为已执行 $this->repository->log($migrationName, $this->getBatchNumber()); $this->note("执行SQL迁移: {$migrationName}"); return; } // 非SQL文件,交给原生逻辑处理 parent::runMigration($file, $method); } // 从文件名提取迁移名称(去掉后缀) protected function getMigrationName($file) { return Str::replaceLast(['.sql', '.php'], '', basename($file)); } }
2. 替换默认的Migrator实例
在app/Providers/AppServiceProvider.php的register方法中,注册自定义的SqlMigrator替换原生服务:
<?php namespace App\Providers; use App\Migration\SqlMigrator; use Illuminate\Database\Migrations\Migrator; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { public function register() { $this->app->singleton('migrator', function ($app) { $repository = $app['migration.repository']; return new SqlMigrator( $repository, $app['db'], $app['files'] ); }); } }
3. 准备SQL脚本
在项目根目录创建database/sql-migrations文件夹,将原生SQL脚本放入其中,文件名必须遵循Laravel迁移命名规范:YYYY_MM_DD_HHMMSS_迁移描述.sql,比如2024_05_20_123456_create_users_table.sql,这样才能保证脚本按顺序执行并被正确追踪。
4. 测试执行
直接运行原生迁移命令即可:
php artisan migrate
命令会自动扫描database/migrations下的PHP迁移和database/sql-migrations下的SQL脚本,只执行未记录到migrations表中的内容。
注意事项
- 回滚支持:上述方案仅处理正向迁移,若需要回滚SQL脚本,需自行扩展
rollback方法,比如约定回滚脚本命名为YYYY_MM_DD_HHMMSS_迁移描述_rollback.sql,再在回滚逻辑中查找并执行对应脚本。 - 事务处理:Laravel原生PHP迁移默认在事务中执行,SQL脚本若需要事务,可自行在脚本中添加
BEGIN和COMMIT,或在runMigration方法中用DB::transaction()包裹执行逻辑。 - 数据库兼容性:确保SQL脚本语法与当前使用的数据库(MySQL/PostgreSQL等)兼容。
- 数据备份:测试前请备份数据库,避免错误SQL导致数据丢失。
内容的提问来源于stack exchange,提问作者ChaseMedallion
相关产品推荐
相关产品推荐

