Laravel 13 数据库迁移进阶
数据库迁移是 Laravel 数据库版本控制的核心功能,提供了优雅的数据库结构管理方式。本文将深入探讨 Laravel 13 数据库迁移的高级用法。
创建迁移
基本迁移
1 2 3
| php artisan make:migration create_users_table php artisan make:migration add_role_to_users_table --table=users php artisan make:migration create_orders_table --create=orders
|
迁移结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
return new class extends Migration { public function up(): void { Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); $table->softDeletes(); }); }
public function down(): void { Schema::dropIfExists('users'); } };
|
表结构操作
创建表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->string('slug')->unique(); $table->text('content'); $table->text('excerpt')->nullable(); $table->enum('status', ['draft', 'published', 'archived'])->default('draft'); $table->integer('views')->default(0); $table->decimal('price', 10, 2)->default(0); $table->boolean('is_featured')->default(false); $table->json('metadata')->nullable(); $table->timestamp('published_at')->nullable(); $table->timestamps(); $table->softDeletes();
$table->index(['status', 'published_at']); $table->fullText(['title', 'content']); });
|
修改表
1 2 3 4 5 6 7
| Schema::table('users', function (Blueprint $table) { $table->string('name', 100)->change(); $table->string('phone')->after('email'); $table->string('role')->default('user')->after('password'); $table->renameColumn('name', 'full_name'); $table->dropColumn('old_column'); });
|
重命名表
1
| Schema::rename('old_table', 'new_table');
|
删除表
1 2
| Schema::dropIfExists('users'); Schema::drop('users');
|
列类型
常用列类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| $table->id(); $table->bigIncrements('id'); $table->uuid('id')->primary(); $table->ulid('id')->primary();
$table->string('name', 100); $table->text('description'); $table->longText('content'); $table->mediumText('summary');
$table->integer('votes'); $table->bigInteger('views'); $table->smallInteger('order'); $table->tinyInteger('status');
$table->float('amount', 8, 2); $table->double('price', 10, 2); $table->decimal('total', 12, 2);
$table->boolean('active'); $table->enum('status', ['pending', 'approved', 'rejected']); $table->set('tags', ['php', 'laravel', 'mysql']);
$table->date('birth_date'); $table->dateTime('published_at'); $table->timestamp('created_at'); $table->timestamp('created_at')->useCurrent(); $table->timestamp('updated_at')->useCurrentOnUpdate();
$table->year('year'); $table->time('start_time');
$table->json('settings'); $table->jsonb('data'); $table->binary('file');
$table->ipAddress('ip'); $table->macAddress('mac'); $table->morphs('taggable'); $table->nullableMorphs('taggable'); $table->rememberToken(); $table->softDeletes(); $table->softDeletesTz();
|
索引
创建索引
1 2 3 4 5 6 7 8 9
| $table->primary('id'); $table->primary(['id', 'parent_id']); $table->unique('email'); $table->unique(['email', 'username']); $table->index('name'); $table->index(['name', 'email']); $table->fullText('content'); $table->fullText(['title', 'content']); $table->spatialIndex('location');
|
命名索引
1 2
| $table->unique('email', 'users_email_unique'); $table->index('name', 'users_name_index');
|
删除索引
1 2 3 4 5
| $table->dropPrimary('id'); $table->dropUnique('users_email_unique'); $table->dropIndex('users_name_index'); $table->dropFullText('posts_content_fulltext'); $table->dropSpatialIndex('locations_location_spatialindex');
|
外键约束
创建外键
1 2 3 4 5 6 7 8 9 10 11 12 13
| Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->foreignId('category_id')->constrained('categories')->cascadeOnDelete(); $table->foreignId('author_id')->nullable()->constrained('users')->nullOnDelete(); $table->foreignUuid('team_id')->constrained(); });
Schema::table('posts', function (Blueprint $table) { $table->unsignedBigInteger('user_id'); $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); });
|
外键操作
1 2 3 4 5 6 7 8 9
| $table->dropForeign('posts_user_id_foreign'); $table->dropForeign(['user_id']);
$table->cascadeOnDelete(); $table->cascadeOnUpdate(); $table->restrictOnDelete(); $table->restrictOnUpdate(); $table->nullOnDelete(); $table->noActionOnDelete();
|
数据库事务
迁移中的事务
1 2 3 4 5 6 7 8 9 10
| use Illuminate\Support\Facades\DB;
public function up(): void { DB::transaction(function () { DB::table('users')->insert([ ['name' => 'Admin', 'email' => 'admin@example.com'], ]); }); }
|
迁移命令
运行迁移
1 2 3 4
| php artisan migrate php artisan migrate --force php artisan migrate --path=/path/to/migration.php php artisan migrate --step
|
回滚迁移
1 2 3
| php artisan migrate:rollback php artisan migrate:rollback --step=5 php artisan migrate:rollback --path=/path/to/migration.php
|
重置迁移
1 2 3 4 5
| php artisan migrate:reset php artisan migrate:fresh php artisan migrate:fresh --seed php artisan migrate:refresh php artisan migrate:refresh --seed
|
查看状态
1
| php artisan migrate:status
|
高级迁移技巧
条件迁移
1 2 3 4 5 6 7 8 9 10 11 12 13
| public function up(): void { Schema::create('posts', function (Blueprint $table) { $table->id(); if (config('database.default') === 'mysql') { $table->charset = 'utf8mb4'; $table->collation = 'utf8mb4_unicode_ci'; } $table->string('title'); }); }
|
检查表/列是否存在
1 2 3 4 5 6 7 8
| if (Schema::hasTable('users')) { }
if (Schema::hasColumn('users', 'email')) { }
if (Schema::hasColumns('users', ['email', 'name'])) { }
|
获取列类型
1
| $type = Schema::getColumnType('users', 'email');
|
数据库连接
1 2 3
| Schema::connection('secondary')->create('users', function (Blueprint $table) { $table->id(); });
|
迁移模板
完整迁移示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <?php
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema;
return new class extends Migration { public function up(): void { Schema::create('orders', function (Blueprint $table) { $table->id(); $table->uuid('order_number')->unique(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->enum('status', [ 'pending', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded' ])->default('pending'); $table->decimal('subtotal', 12, 2)->default(0); $table->decimal('tax', 12, 2)->default(0); $table->decimal('shipping', 12, 2)->default(0); $table->decimal('discount', 12, 2)->default(0); $table->decimal('total', 12, 2)->default(0); $table->string('currency', 3)->default('USD'); $table->json('shipping_address'); $table->json('billing_address'); $table->json('metadata')->nullable(); $table->text('notes')->nullable(); $table->timestamp('ordered_at')->useCurrent(); $table->timestamp('processed_at')->nullable(); $table->timestamp('shipped_at')->nullable(); $table->timestamp('delivered_at')->nullable(); $table->timestamp('cancelled_at')->nullable(); $table->timestamps(); $table->softDeletes(); $table->index(['user_id', 'status']); $table->index('created_at'); $table->index('ordered_at'); });
Schema::create('order_items', function (Blueprint $table) { $table->id(); $table->foreignId('order_id')->constrained()->cascadeOnDelete(); $table->foreignId('product_id')->constrained()->restrictOnDelete(); $table->string('product_name'); $table->string('product_sku'); $table->integer('quantity')->default(1); $table->decimal('unit_price', 12, 2); $table->decimal('total_price', 12, 2); $table->json('options')->nullable(); $table->timestamps(); $table->index(['order_id', 'product_id']); }); }
public function down(): void { Schema::dropIfExists('order_items'); Schema::dropIfExists('orders'); } };
|
最佳实践
1. 使用描述性名称
1 2 3
| php artisan make:migration create_orders_table php artisan make:migration add_status_to_orders_table php artisan make:migration remove_deprecated_column_from_users_table
|
2. 保持迁移可逆
1 2 3 4 5 6 7 8 9 10 11 12 13
| public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('phone')->nullable(); }); }
public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('phone'); }); }
|
3. 使用外键约束
1 2 3 4
| $table->foreignId('user_id') ->constrained() ->cascadeOnDelete() ->cascadeOnUpdate();
|
4. 添加适当的索引
1 2
| $table->index(['status', 'created_at']); $table->unique(['user_id', 'product_id']);
|
总结
Laravel 13 的数据库迁移系统提供了强大而灵活的数据库版本控制能力。通过合理使用迁移,可以轻松管理数据库结构变更,确保团队协作时数据库结构的一致性。记住使用描述性的迁移名称、保持迁移可逆、合理使用索引和外键约束,并在生产环境中小心执行破坏性操作。迁移是 Laravel 数据库开发的核心工具,掌握它对于构建可维护的应用程序至关重要。