【Laravel】migrate時の「Foreign key constraint is incorrectly formed」エラーの解消方法

laravelでmigrate機能はとても便利です。
ライブラリ全般に言えることではありますが、1からクエリを書かないので管理のしやすさが格段に上がります。
しかし、生成されたクエリにエラーが発生した場合は、生でクエリを作るのと勝手が違って戸惑う場面もあります。

さて、
今回は、外部キー制約を扱う際に発生しうるエラーと、その対応方法について触れます。

イントロダクション

この記事で得られること

  • migrate時の「Foreign key constraint is incorrectly formed」エラーの解消方法がわかる

環境

種類 バージョン
laravel 5.5

状況

例えば、users テーブルにcodeというカラムを追加し、 mappingというテーブルから、codeに対して外部キーを設定するとします。

その場合、次のように設定をしている可能性があります。

  • users
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('code');
            $table->string('password')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }
  • mapping
    public function up()
    {
        Schema::create('mappings', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('code');
            $table->foreign('code')->references('code')->on('users');
            $table->timestamps();
        });
    }

実行結果

上記migrateファイルを用意した状態で、migrateを実行すると次のようなエラーが返ってきます。

$ php artisan migrate
SQLSTATE[HY000]: General error: 1005 Can't create table `test`.`#sql-1_b` (errno: 150 "Foreign key constraint is incorrectly formed") (SQL: alter table `mappings` add constrain
  t `mappings_code_foreign` foreign key (`code`) references `users` (`code`))

原因

原因は2つあります。

データの型が適切ではない。

数字型で外部キーを設定する場合は、符号なしの整数型(unsignedInteger)で指定する必要があります。 前項の例では、外部キーの対象が、符号付きの整数型(integer)を選んでいるためです。

$table->integer('code');

データがユニークではない。

外部キーにする場合、対象のカラムは重複してはいけません
対象のカラムにUNIQUE 制約に設定する必要があります。

解決方法

前項の例を参考に修正箇所について触れます。
外部キーを対象元、対象先の文字型を下記のように変えます。

  • users
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('code')->unique();
            $table->string('password')->nullable();
            $table->rememberToken();
            $table->timestamps();
        });
    }
  • mapping
    public function up()
    {
        Schema::create('mappings', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('code');
            $table->foreign('code')->references('code')->on('users');
            $table->timestamps();
        });
    }

参考サイト