[筆記] Laravel One to Many 遇到 a foreign key constraint fails
碰到錯誤訊息了
(以下 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 tofalse
. If your primary key is not an integer, you should set the protected$keyType
property on your model tostring
.
擷取 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