概要
phpunitでテストを書いているときに、factoryでモデルを作ってそのIDを利用するみたいなコードを書きました。
$user = factory(User::class)->create();
$id = $user->id;
しかしなぜかidがないよ!と怒られたり、idが0になっていたりしました。DBにはきちんと保存されているのに。
結論
モデルにはきちんとincrementingプロパティを指定しましょう。
説明
id(PK)は基本的にオートインクリメントになるように設定していると思いますが、テーブルによってはオートインクリメントしていないものもあります。
オートインクリメントしないモデルには$incrementeng = falseを指定しましょう。(デフォルトではtrueになっています。)
また、中間テーブルでオートインクリメントを指定している、かつPivotを継承している場合はtrueを指定しましょう。Pivotはデフォルトではfalseに設定されています。
余談
なんでidが0になったりするかというと、$incrementingの値で処理を分けていて、
if ($this->getIncrementing()) {
$this->insertAndSetId($query, $attributes);
}
// If the table isn't incrementing we'll simply insert these attributes as they
// are. These attribute arrays must contain an "id" column previously placed
// there by the developer as the manually determined key for these models.
else {
if (empty($attributes)) {
return true;
}
$query->insert($attributes);
}
オートインクリメントのときはレコードをinsertした後に設定されたidを取得してくるという動作をしています。
insertAndSetIdのなかにinsertGetIdという処理があり、最終的にPDOのlastInsertIdというメソッドを呼んでidを取得しています。
lastInsertIdというメソッド名だけ見ると、オートインクリメントじゃなくてもidを取ってきてくれそうですが、このメソッドはDBの種類によって挙動が変わるっぽいです。
mysqlだとauto_incrementが設定されていないとだめみたいです。
なので、idをオートインクリメントにしていないのにモデルが$incrementing = true'の状態だと、lastInsertId`が呼ばれますがidは0で返ってくることになります。(postgresは未検証)