Laravelを触り始めて約一ヶ月の初心者です
つまづいたのでメモ
結論としてはLaravelクエリビルダのdelete(というかMySQLのDELETE)ではauto_incrementがリセットされないという仕様が分かってなかったっていう話です
環境
Laravel 5.8
1つのUserに対して多数のItemが紐付く構造
userを削除した際はitemも削除するよう外部キーを持たせてあります
public function up()
{
Schema::create('items', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('title');
$table->bigInteger('user_id')->unsigned();
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
ユーザテーブルのマイグレーションファイルはLaravelデフォルトなので省略
↓こちらがseeder
public function run()
{
DB::table('users')->delete();
App\User::create([
'name' => 'hoge',
'email' => 'hoge@example.com',
'password' => Hash::make('hogehoge'),
]);
App\User::create([
'name' => 'hage',
'email' => 'hage@example.com',
'password' => Hash::make('hagehage'),
]);
}
public function run()
{
DB::table('items')->delete();
App\Item::create([
'title' => '君という花',
'user_id' => '1',
]);
App\Item::create([
'title' => 'リライト',
'user_id' => '2',
]);
}
2つともseed時は一回deleteで全消ししてからデータを挿入します
一回migrate:refreshを挟まないとdb:seed出来ない問題
refreshせずにシーディングを実行すると1452エラーが出る
$ php artisan db:seed
Seeding: UsersTableSeeder
Seeding: ItemsTableSeeder
Illuminate\Database\QueryException : SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails
refreshを挟むと何故か成功する
php artisan migrate:refresh
したあとにphp artisan db:seed
を再度実行
$ php artisan migrate:refresh
Rolling back: 2019_06_10_190034_create_items_table
Rolled back: 2019_06_10_190034_create_items_table
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_100000_create_password_resets_table
Rolling back: 2014_10_12_000000_create_users_table
Rolled back: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table
Migrating: 2019_06_10_190034_create_items_table
Migrated: 2019_06_10_190034_create_items_table
$ php artisan db:seed [~/dev/app/gaogao_gate/likes]
Seeding: UsersTableSeeder
Seeding: ItemsTableSeeder
Database seeding completed successfully.
(逆に)なんでや…
再度db:seedを実行、やっぱり1452のエラーが出る…
UserTableを確認してみた
mysql> select id from users;
+----+
| id |
+----+
| 3 |
| 4 |
+----+
2 rows in set (0.01 sec)
id途中から始まってるやんけ!
どうやらdeleteではauto_incrementに設定したidがリセットされない様子
既に存在しない親のidをitemに与えようとして1452エラーが出ていたようです
どうしよう
公式ドキュメント見てdeleteじゃなくてtruncate使えばええやん!と思ったけどそうでも無い様子 (1701エラーが出る)
外部キーがあると使えない模様…(rollbackもできなくなる)
deleteと同時にauto_incrementを1に戻してしまえばよい
そんな都合のいいクエリは無いみたいなのでdeleteの下にSQLを1行追加
DB::table('users')->delete();
DB::unprepared("ALTER TABLE users AUTO_INCREMENT = 1 ");
これでとりあえず(ゴリ押し)解決したみたいです👏
ていうかItemSeederで親のidを数字で与えているのがそもそもダメなのでは…
テストデータだから許して
追記
以下、真偽不明になる事案があったため気にしないでください(検証すればええやんって話ですが…)
user_id
が途中から始まるのは明示的に指定しなかった場合に起きる模様、なので
public function run()
{
DB::table('users')->delete();
App\User::create([
'id' => '1',
'name' => 'hoge',
'email' => 'hoge@example.com',
'password' => Hash::make('hogehoge'),
]);
App\User::create([
'id' => '2',
'name' => 'hage',
'email' => 'hage@example.com',
'password' => Hash::make('hagehage'),
]);
}
のように明示的にid
を指定するとAUTO_INCREMENTS
を無視できるみたいです