設定 FOREIGN_KEY_CHECKS
(1) 如果你在有設定 foreign 的相關 table 做 seeder 的時候,假設出現以下的 error: SQLSTATE[42000]: Syntax error or access violation: 1701 Cannot truncate a table referenced in a foreign key constraint ....
會有這樣的問題是因為 seed 的時候,就會立刻去檢查某個 schema 的外鍵,可是外鍵當時可能還沒建立。
那表示你需要在 seeder 在 inser 資料前後,做 FOREIGN_KEY_CHECKS 的切換 (畫底線那兩行),inser資料前先設為 0,insert 資料後設為 1 :
小心,unsigned()!
(2) 『當外鍵有參照到自動增量時,記得設定外鍵為 unsigned 型態。』- from http://laravel.tw/docs/4.2/schema
這句話的意思是,如果某張表的 key 是參考到另一張表的鍵而且是 auto_increment 時,要記得將那個外鍵設定為 unsigned()
範例,假這個 id 會在別的 table 某欄位參考到,那麼就要記得 unsigned:
$table->increments('id')->unsigned();
這是比較多新手會忘記的地方。
phpunit 跑 SQLite
(3) 補充一下關於 (1) FOREIGN_KEY_CHECKS 這件事情,因為在 phpunit 測試 ORM 時,有時會用 sqlite 而不是 MySQL,如果照樣使用 (1) 的 FOREIGN_KEY_CHECKS 的方式會報錯,另外也不建議使用 DB::enableForeignKeyCheck() 或是 DB::disableForeignKeyCheck() (這是我從 issue 找到的方法....),試一下發現 sqlite 一樣看不懂,而且還會噴出這樣的錯誤: rrorException: call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Database\SQLiteConnection' does not have a method 'disableForeignKeyCheck'
所以我目前找到比較好的方式(可以 mysql 跟 sqlite 兩全其美) 就是把 (1) 改成這個樣子...我其實說不上來這樣的修改好不好,如果有快速,乾淨解法,拜託讓我知道 QQ
2015 08-31 補充:
有時候在跑測試的時候,會出現 Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 near "SET": syntax error (SQL: SET FOREIGN_KEY_CHECKS=0),這也是因為測試時的 db 環境是 Sqlite,所以 check foreign 的方式也不太一樣。
https://laracasts.com/discuss/channels/general-discussion/sql-hy000-error-when-seeding-phpunit-tests
這邊有個參考作法,直接從 DatabaseSeeder 動手腳,感覺比較乾淨,用 DB::getDriverName() 的方法感覺也比 instanceof Illuminate\Database\SQLiteConnection 更適合。
如果再跑測試的時候遇到 SQLSTATE[HY000]: General error: 1 too many SQL variables,
找了一下只知道是因為: this is a limitation of sqlite: 999 parameters.。解法還在找 :P
(1) 如果你在有設定 foreign 的相關 table 做 seeder 的時候,假設出現以下的 error: SQLSTATE[42000]: Syntax error or access violation: 1701 Cannot truncate a table referenced in a foreign key constraint ....
會有這樣的問題是因為 seed 的時候,就會立刻去檢查某個 schema 的外鍵,可是外鍵當時可能還沒建立。
那表示你需要在 seeder 在 inser 資料前後,做 FOREIGN_KEY_CHECKS 的切換 (畫底線那兩行),inser資料前先設為 0,insert 資料後設為 1 :
// 假設這是某隻 seeder
// 這隻 seeder 的某個欄位是別張表的外鍵
// XXXXTableSeeder.php
<?php
use Illuminate\Database\Seeder;
class UserTableSeeder extends Seeder
{
public function run()
{
// disable foreign key constraints
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
DB::table('user')->truncate();
$users = [
// data....
];
DB::table('user')->insert($users);
// disable foreign key constraints
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
}
// 這隻 seeder 的某個欄位是別張表的外鍵
// XXXXTableSeeder.php
<?php
use Illuminate\Database\Seeder;
class UserTableSeeder extends Seeder
{
public function run()
{
// disable foreign key constraints
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
DB::table('user')->truncate();
$users = [
// data....
];
DB::table('user')->insert($users);
// disable foreign key constraints
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
}
小心,unsigned()!
(2) 『當外鍵有參照到自動增量時,記得設定外鍵為 unsigned 型態。』- from http://laravel.tw/docs/4.2/schema
這句話的意思是,如果某張表的 key 是參考到另一張表的鍵而且是 auto_increment 時,要記得將那個外鍵設定為 unsigned()
範例,假這個 id 會在別的 table 某欄位參考到,那麼就要記得 unsigned:
$table->increments('id')->unsigned();
這是比較多新手會忘記的地方。
phpunit 跑 SQLite
(3) 補充一下關於 (1) FOREIGN_KEY_CHECKS 這件事情,因為在 phpunit 測試 ORM 時,有時會用 sqlite 而不是 MySQL,如果照樣使用 (1) 的 FOREIGN_KEY_CHECKS 的方式會報錯,另外也不建議使用 DB::enableForeignKeyCheck() 或是 DB::disableForeignKeyCheck() (這是我從 issue 找到的方法....),試一下發現 sqlite 一樣看不懂,而且還會噴出這樣的錯誤: rrorException: call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Database\SQLiteConnection' does not have a method 'disableForeignKeyCheck'
所以我目前找到比較好的方式(可以 mysql 跟 sqlite 兩全其美) 就是把 (1) 改成這個樣子...我其實說不上來這樣的修改好不好,如果有快速,乾淨解法,拜託讓我知道 QQ
if (DB::connection() instanceof Illuminate\Database\SQLiteConnection) {
DB::statement(DB::raw('PRAGMA foreign_keys=0'));
} else {
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
}
// insert data
if (DB::connection() instanceof Illuminate\Database\SQLiteConnection) {
DB::statement(DB::raw('PRAGMA foreign_keys=1'));
} else {
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
DB::statement(DB::raw('PRAGMA foreign_keys=0'));
} else {
DB::statement('SET FOREIGN_KEY_CHECKS = 0');
}
// insert data
if (DB::connection() instanceof Illuminate\Database\SQLiteConnection) {
DB::statement(DB::raw('PRAGMA foreign_keys=1'));
} else {
DB::statement('SET FOREIGN_KEY_CHECKS = 1');
}
2015 08-31 補充:
有時候在跑測試的時候,會出現 Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1 near "SET": syntax error (SQL: SET FOREIGN_KEY_CHECKS=0),這也是因為測試時的 db 環境是 Sqlite,所以 check foreign 的方式也不太一樣。
https://laracasts.com/discuss/channels/general-discussion/sql-hy000-error-when-seeding-phpunit-tests
這邊有個參考作法,直接從 DatabaseSeeder 動手腳,感覺比較乾淨,用 DB::getDriverName() 的方法感覺也比 instanceof Illuminate\Database\SQLiteConnection 更適合。
如果再跑測試的時候遇到 SQLSTATE[HY000]: General error: 1 too many SQL variables,
找了一下只知道是因為: this is a limitation of sqlite: 999 parameters.。解法還在找 :P
沒有留言:
張貼留言
若你看的文章,時間太久遠的問題就別問了,因為我應該也忘了... XD