Laravel - 迁移,表结构修改 - 正确方法

Posted

技术标签:

【中文标题】Laravel - 迁移,表结构修改 - 正确方法【英文标题】:Laravel - migration, table structure modification - correct way 【发布时间】:2020-02-02 16:23:43 【问题描述】:

我现在的桌子是

Schema::create('students', function (Blueprint $table) 
    $table->bigIncrements('id');
    $table->string('first_name', 255);
    $table->string('last_name', 255);
    $table->enum('gender', ['m', 'f']);
    $table->date('date_of_birth');
    $table->integer('roll_number');
    $table->char('section', 1);
    $table->integer('class');
    $table->unsignedBigInteger('school_id');            
    $table->string('photo')->nullable;
    $table->timestamps();

    $table->foreign('school_id')
        ->references('id')->on('schools')
        ->onUpdate('cascade')->onDelete('cascade');

    $table->unique(['roll_number', 'section', 'class', 'school_id']);
);

标准

Schema::create('standards', function (Blueprint $table) 
       $table->bigIncrements('id');
       $table->string('name');
       $table->unsignedBigInteger('school_id');
       $table->timestamps();

       $table->foreign('school_id')
       ->references('id')->on('schools')
       ->onUpdate('cascade')->onDelete('cascade');
    );

部分

Schema::create('sections', function (Blueprint $table) 
    $table->bigIncrements('id');
    $table->char('name', 1);
    $table->unsignedBigInteger('standard_id');
    $table->timestamps();

    $table->foreign('standard_id')
    ->references('id')->on('standards')
    ->onUpdate('cascade')->onDelete('cascade');
);

现在我有标准和部分表,这些表中的外键将替换现有结构中的类和部分列,并保持 roll_numbersection_idstandard_idschool_id 的组合是唯一的。

我试过了

 public function up()
    
        Schema::table('students', function (Blueprint $table) 
            $table->dropUnique(['roll_number', 'section', 'class', 'school_id']);

            $table->dropColumn('section');
            $table->dropColumn('class');
            $table->unsignedBigInteger('standard_id')->after('roll_number');
            $table->unsignedBigInteger('section_id')->after('standard_id');

            $table->foreign('standard_id')->references('id')->on('standards')
            ->onUpdate('cascade')->onDelete('cascade');
            $table->foreign('section_id')->references('id')->on('sections')
            ->onUpdate('cascade')->onDelete('cascade');

            $table->unique(['roll_number', 'standard_id', 'section_id', 'school_id']); // unique combination
        );
    

但它似乎不起作用。

错误

Illuminate\Database\QueryException : SQLSTATE[23000]: 完整性 违反约束:1452 无法添加或更新子行:外国 键约束失败(myapp_extra.#sql-2f78_29d, CONSTRAINT students_standard_id_foreign 外键 (standard_id) 参考 NCES standards (id) ON DELETE CASCADE ON UPDATE CASCADE) (SQL: alter table students add constraint students_standard_id_foreign 删除时外键 (standard_id) 引用 standards (id) 级联更新级联)

  at \myapp\vendor\laravel\framework\src\Illuminate\Database\Connection.php:664

注意:标准和部分表是在进行此迁移之前创建的,因此这两个列都可用。

【问题讨论】:

您遇到的错误是什么? @SalmanZafar 请重新加载,我已经添加了错误。 您能提供sectionsstandards 架构吗? @MiladBarazandeh 请重新加载页面,我刚刚更新了它。 【参考方案1】:

为什么会这样?

发生此错误是因为您正在向已有行的表中插入一个没有默认值且没有NULLABLE 的新列。

当你这样做时,mysql 会将它的值设置为0,所以你所有的表都将standard_idsection_id 设置为0,所以当你尝试添加外部索引时,它会失败,因为0 不是您的 standards/sections 表上的有效 ID。

那么如何解决呢?

你有一些方法可以解决这个问题:

1st:设置默认值

如果对您的应用有意义,您可以为您的列设置一个默认(有效)值,这样外键就不会失败:

        $table->unsignedBigInteger('standard_id')->default(1)->after('roll_number');
        $table->unsignedBigInteger('section_id')->default(1)->after('standard_id');

大多数时候它并不是那么简单,所以你需要动态地定义值

2nd:动态设置值

如果您有一些逻辑可以为这些新列设置默认值,则可以将迁移分为两个步骤:

// Add the fields first
Schema::table('students', function (Blueprint $table) 
        $table->dropUnique(['roll_number', 'section', 'class', 'school_id']);

        $table->dropColumn('section');
        $table->dropColumn('class');
        $table->unsignedBigInteger('standard_id')->after('roll_number');
        $table->unsignedBigInteger('section_id')->after('standard_id');


App\Students::get()->each(function($student) 
    // Apply your logic here
    $student->standard_id = 3;
    $student->section_id = 3;
    $student->save();
);

// Now you can add your foreign keys.
Schema::table('students', function (Blueprint $table) 
        $table->foreign('standard_id')->references('id')->on('standards')
        ->onUpdate('cascade')->onDelete('cascade');
        $table->foreign('section_id')->references('id')->on('sections')
        ->onUpdate('cascade')->onDelete('cascade');

        $table->unique(['roll_number', 'standard_id', 'section_id', 'school_id']); // unique combination
);

3rd:使字段可以为空

如果您只是不知道或没有此字段的默认值,那么您的字段应该可以为空:

    $table->unsignedBigInteger('standard_id')->nullable()->after('roll_number');
    $table->unsignedBigInteger('section_id')->nullable()->after('standard_id');

【讨论】:

谢谢 Elias,我在想如何随机给出现有标准的 id 或部分的 id,但似乎只是不想在迁移文件中写任何插入查询,但似乎没有其他选项。

以上是关于Laravel - 迁移,表结构修改 - 正确方法的主要内容,如果未能解决你的问题,请参考以下文章

laravel 5.4 迁移问题

laravel框架数据迁移

Laravel 8.X Eloquent 中数据库迁移的正确外键约束语法是啥?

Laravel 迁移错误号:150“外键约束格式不正确”

(Laravel) 运行迁移时出现不正确的日期时间格式错误

Laravel 迁移错误:150“外键约束格式不正确”