You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 21:10:02