公式の書き方を参照すると、外部キー制約を追加する際、マイグレーションファイルに下記を記述する
# userテーブルと紐付ける条件で作成した
Schema::create('posts', function (Blueprint $table) {
$table->id('post_id');
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
# または
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
- 今回は、CASCADEの設定も行っている
- 著者は、userテーブルを題材として使用し、今回の説明を行う
上記の記述の場合、userテーブルが下記の状態であれば、マイグレーションを正常に行う事ができる
id | name | emeil |
---|---|---|
1 | test1 | aaa@example.com |
2 | test2 | aaa@aaa.com |
しかし、下記のテーブル構造だった場合、上記の外部キー制約の書き方では、マイグレーションが正常に動作しない
(マイグレーションを実行するとエラーとなる)
user_id | name | emeil |
---|---|---|
1 | test1 | aaa@example.com |
2 | test2 | aaa@aaa.com |
エラー文は、下記が表示される
Illuminate\Database\QueryException
SQLSTATE[HY000]: General error: 1824 Failed to open the referenced table 'user_id' (SQL: alter table `posts` add constraint `posts_user_id_foreign` foreign key (`user_id`) references `user_id` (`id`) on delete cascade)
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:760
756▕ // If an exception occurs when attempting to run a query, we'll format the error
757▕ // message to include the bindings with SQL, which will make this exception a
758▕ // lot more helpful to the developer instead of just the database's errors.
759▕ catch (Exception $e) {
➜ 760▕ throw new QueryException(
761▕ $query, $this->prepareBindings($bindings), $e
762▕ );
763▕ }
764▕ }
+9 vendor frames
10 database/migrations/2023_01_04_231333_create_posts_table.php:22
Illuminate\Support\Facades\Facade::__callStatic("create")
+36 vendor frames
47 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
DBにアクセスして、エラーで表示されているSQL文を修正し、修正したクエリーを叩けば正常にDBを動かす事ができるが、それではマイグレーションファイルを使う意味がなくなってしまう。また、実際の業務での開発を想定した場合、マイグレーションが正常に動作しないのは、明らかに他の人の迷惑にも繋がる。
/* エラーで出力されたSQL文 */
alter table `posts`
add constraint `posts_user_id_foreign`
foreign key (`user_id`) references `user_id` (`id`) on delete cascade
/* エラー文で出力されたSQLを修正したSQL文 */
alter table `posts`
add constraint `posts_user_id_foreign`
foreign key (`user_id`) references `user_id` (`user_id`) on delete cascade
SQL文を見ると references
の ()
に任意で指定したid名が入力できれば、正常に動作する事が分かる。
しかし、マイグレーションファイルに正常に動作するSQLを記述する方法が、公式ドキュメントを確認しても記述されていなかった。
ここで、各メソッドの裏側の処理を確認した際、constrained()
の裏側の処理にヒントがあった。
/**
* Create a foreign key constraint on this column referencing the "id" column of the conventionally related table.
*
* @param string|null $table
* @param string $column
* @return \Illuminate\Database\Schema\ForeignKeyDefinition
*/
public function constrained($table = null, $column = 'id')
{
return $this->references($column)->on($table ?? Str::of($this->name)->beforeLast('_'.$column)->plural());
}
constrained()
の第二引数に任意で指定したid名を入力すれば、正常に動作するのではと思い、第二引数にidを記述したら、正常にマイグレーションができるようになりました。
下記が修正したコード
# 修正前
- $table->foreignId('user_id')->constrained('users')->onDelete('cascade');
# 修正後
+ $table->foreignId('user_id')->constrained('users', 'user_id')->onDelete('cascade');
同じ問題で困っている人がいましたら、是非参考にしてみて下さい。