[筆記] Laravel One to Many 遇到 a foreign key constraint fails

By Chad250 CC-BY-SA-4.0

碰到錯誤訊息了

(以下 Model 與 Table 名稱為示意,有可能因為手工改寫而有出入,看起來怪怪的地方請跟我說)

在儲存 Category、Product 兩 table 的 One to Many 資料時,我收到的錯誤訊息如下:

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`store`.`products`, CONSTRAINT `products_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)) (SQL: insert into `products` (`name`, `category_id`) values (商品 P, 0))

發生錯誤的程式碼簡化後類似這樣:

$category = new Category;
$category->id = "category01";
$category->name = "分類 C";
$category->save();

$product = new Product;
$product->name = "商品 P";
$category->products()->save($product);

檢查了一遍 Category 的 Model 已經設好 hasMany

public function products()
{
    return $this->hasMany('App\Product');
}

Product 的 Model 中 belongsTo 也設定正確:

public function category()
{
    return $this->belongsTo('App\Category');
}

問題在這

這時我們從上面錯誤訊息可以注意到 category_id 的值是「0」

values (商品 P, 0)

這個 category_id 照程式碼來看應該要是「category01」,
但我們要儲存 one to many 時,卻在組成 SQL 語法的時候變成了「0」?

我們來看 Category table 的 Migration。
Key 的型態為 String,當然也就不是 auto increment。

$table->string('id')->primary();

解決方法

依據 [Eloquent 文件] 的說明(https://laravel.com/docs/5.6/eloquent):

If you wish to use a non-incrementing or a non-numeric primary key you must set the public $incrementing property on your model to false. If your primary key is not an integer, you should set the protected $keyType property on your model to string.

擷取 Laravel 道場的中文翻譯

如果你希望使用非遞增或非數字的主鍵,務必把模型上的 public $incrementing 屬性設定為 false。如果你的主鍵不是一個整數,你應該將模型上的 protected $keyType 屬性設定為 string

所以我們要在 Category 的 Model 中新增下面的程式碼:

public $incrementing = false;
protected $keyType = 'string';

這樣就可以在儲存 one to many 的時候,抓取到正確的 category_id 資料了!

首圖:By Chad250 (Own work), CC BY-SA 4.0, via Wikimedia Commons