Laravel 本身不提供导出 Schema 为 SQL 或对比数据库结构差异的原生命令,需依赖 mysqldump 或 Doctrine DBAL 等第三方工具;schema:dump 生成的是 PHP 快照而非可执行 SQL。

直接说结论:Laravel 本身不提供「导出当前 Schema 为 SQL 文件」或「对比两个数据库结构差异」的原生命令。想实现数据库结构同步,必须借助第三方工具或手动组合命令 + 脚本,否则容易误删表、丢数据。
为什么 php artisan schema:dump 不是你要的 SQL 文件
Laravel 9.2+ 引入了 schema:dump,但它生成的是 PHP 格式的快照(database/schema/mysql.php),用于加速迁移加载,不是标准 SQL。它不包含 CREATE TABLE 语句,也不能被 MySQL 直接执行。
常见误解是运行后去翻这个文件,试图复制粘贴建表语句——结果发现全是 $table->string('name') 这类 PHP 调用,完全没法用。
- 它只在
APP_ENV=production下默认启用,本地开发默认不生成 - 它不记录索引、外键的完整定义(比如
ON DELETE CASCADE可能丢失) - 它不能反向生成差异,也不能和另一个库做比对
用 mysqldump --no-data 快速导出结构 SQL
最轻量、最可靠的方式是绕过 Laravel,用数据库原生命令导出结构。适用于需要交付建库脚本、初始化测试环境、或做版本间 DDL 对比的场景。
示例(MySQL):
mysqldump -u root -p --no-data --skip-triggers --skip-routines your_database_name > schema_202405.sql
关键参数说明:
-
--no-data:只导出表结构,不导数据 -
--skip-triggers和--skip-routines:避免触发器/存储过程干扰结构一致性(Laravel 迁移一般也不管这些) - 导出的 SQL 可直接用
mysql -u root -p your_db 重建空库
注意:该命令依赖系统已安装 mysqldump,Docker 环境需进容器执行,或用 docker exec 包装。
用 doctrine/dbal 实现 PHP 层结构对比
如果必须在 Laravel 内部做结构比对(比如 CI 中校验迁移是否覆盖所有字段),得靠 doctrine/dbal —— Laravel 的 Schema Builder 底层就用它。
先确保已安装(Laravel 9+ 通常已带):
composer require doctrine/dbal
然后写一个 Artisan 命令,用 Doctrine\DBAL\Schema\Comparator 对比两个连接的 Schema:
$fromSchema = $connection->getSchemaManager()->createSchema();
$toSchema = $connection->getSchemaManager()->createSchema(); // 或从另一连接获取
$comparator = new \Doctrine\DBAL\Schema\Comparator();
$diff = $comparator->compare($fromSchema, $toSchema);
foreach ($diff->toSql($connection->getDatabasePlatform()) as $sql) {
echo $sql . ";\n";
}
实际使用时要注意:
- 必须保证两个连接指向兼容的数据库类型(比如都是 MySQL 8.0),跨类型(MySQL ↔ PostgreSQL)对比会失败
- DBAL 的字段类型映射可能和 Laravel 迁移不完全一致(例如
$table->json()在 MySQL 5.7 是JSON,但 DBAL 可能识别为TEXT) - 外键约束名若未显式指定(如
foreignIdFor(User::class)),DBAL 生成的约束名可能每次不同,导致误报差异
同步生产库结构到本地?别直接跑 migrate:fresh
很多开发者想“把线上库结构拉下来”,第一反应是 php artisan migrate:fresh --seed。这非常危险:它会删掉所有表再重跑迁移,而线上迁移文件很可能早已删掉旧字段、改过表名,导致本地结构和线上实际不一致。
更稳妥的做法是:
- 从生产库导出结构 SQL(用上一节的
mysqldump --no-data) - 在本地新建空库,导入该 SQL
- 再运行
php artisan migrate:status查看哪些迁移没执行,针对性补跑(而不是全量刷新) - 如果线上有手工执行的 DDL(比如 DBA 加了索引),这些操作不会出现在迁移文件里,必须人工核对并补进迁移
真正容易被忽略的点是:结构同步 ≠ 迁移文件同步。Laravel 的迁移只是其中一种变更记录方式,而生产库的真实结构,永远以数据库元数据为准。










