Laravel 設定 foreign 要注意的事情

設定 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 :

// 假設這是某隻 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');
    }
}


小心,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');
}

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

留言

這個網誌中的熱門文章

[Android] 筆記 手機上測試自己的 APP

解決fatal: Not a git repository (or any of the parent directories): .git錯誤

[Android 筆記] 設定 ImageView 的圖檔來源