😺

Laravel カラム追加 外部キーカラムの追加と削除

2021/01/19に公開

はじめに

・カラムを後から追加したい
・外部制約キーをつけたカラムを追加したい

向けの内容となっています

#マイグレーション 外部キーカラムを追加

1.migrationファイルの作成
例) offersテーブルにuser_idカラムを追加したい場合

migration
$ php artisan make:migration add_user_id_to_offers_table --table=offers

命名規則は add_(カラム名)to(テーブル名)_table --table=(テーブル名)
でファイル名を作成します

--table=(テーブル名)の部分では、カラムを追加のテーブルを指定して行っています。

migration
public function up()
{
    Schema::table('offers', function (Blueprint $table) {
      $table->integer('user_id')->unsigned();
      $table->foreign('user_id')->references('id')->on('users')->OnDelete('cascade');
    });
}
$ php artisan migrate

さあ、これでマイグレーションするとカラム作成だ!

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails

と思いきや,うまく外部制約キーのカラムがうまく作成できません。

調べてみると、原因は、「users.idに存在しないデータがoffers.user_idに存在しているから」だそうです。
上記のマイグレーションをしたときはoffersテーブルにはuser_idのカラムが入っていましたが、user_idが0でした。

もともと外部キー制約は
「参照元フィールドに存在するデータのみ参照先フィールドのデータに存在できる」
という仕様です。

そもそも,user_idが存在しない場合にoffers.user_idが存在しているのは外部キーの設定ができるはずもありません。

解決方法

今回の場合の対策として

  1. 整合性の取れないデータはdeleteする
  2. usersにデータを追加する
  3. offersテーブルのuser_idをnullにする

外部制約のキーでデータが一致しないとエラーになります。

そのときに
php artisan migrate:refresh
php artisan migrate:fresh
など一旦データを削除してまたmigrateをするとうまくいきますが、問題点として 

すでにあるデータを削除してしまう
毎回、データを削除しないといけない
などがあります。

本来なら最初にちゃんとテーブル設計したらと思いますが、開発の途中で機能追加するときもこういった場面があるのではないかなと思い記事を書いてみました。

データを挿入するなら
・seedやfactoryを使ってデモデータを挿入する

などのやり方があります。

今回僕がやった方法は, 外部制約キーにnullableを追加して解決しました。

migrate
public function up()
    {
        Schema::table('offers', function (Blueprint $table) {
            $table->integer('user_id')->unsigned()->nullable();
            $table->foreign('user_id')->references('id')->on('users')->OnDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('offers', function (Blueprint $table) {
            $table->dropForeign('offers_user_id_foreign');
        });

        // Schema::table('offers', function (Blueprint $table) {
        //     $table->dropColumn('user_id');
        // });
    }

nullable()を追加すれば、migrateがうまくいきます。
down()のところには外部制約キーを削除できるように
$table->dropForeign('offers_user_id_foreign');

に設定します。

ただ、外部キーをNullにするのはどうかなぁと考えてしまいます。
ここら辺はSQLの知識が必要だなと思っています。

外部キー制約カラムの追加を試している方はぜひ参考にしてみてください。

参考

Laravel7.x データベース: マイグレーション

Laravelで、外部キー制約の処理で SQLSTATE[23000] エラーが出て、マイグレートできません。

Discussion