Eloquentのupsert()って?
これです
https://readouble.com/laravel/8.x/ja/eloquent.html#upserts
Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
同じEloquentでupsertするメソッドだとupdateOrCreate()とかありますね。
比較的最近(8.10以降?)実装されたものらしく、またドキュメント読んだだけではイマイチ使い方がわからなかったので、自分で検証したことも含め備忘録がてら書き残しておこうと思います。
upsert()についてもですが、Laravel自体始めて日が浅いため何か間違ってる箇所があればご指摘をお願いします。
環境
PHP:7.4
Laravel:8.13.0
解説
基本
$data = [
['id' => 1,'departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['id' => 2,'departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
]
Flight::upsert($data, ['id'], ['price']);
ドキュメントの例に少し手を加えたものです。
第一引数に更新したい内容、第二引数にプライマリーキー、第三引数に更新したいカラムをそれぞれ配列で渡します。第二引数はSQLServer以外必須だそうです。
個人的にドキュメント読んでわかりにくいと思ったのが第二引数に指定したカラムを第一引数の配列に含めるかどうか。
結論から言うと含めます。ないと確定でInsertになってしまいます。
例ではidカラムをプライマリーキーとして想定しているので第二引数に['id']
と書いていますが、第一引数の$dataという配列にもidカラムの値を入れています。
第二引数でプライマリーキーさえ指定すれば第一引数の配列に含めなくてもいい感じにInsertかUpdateか判断してくれるのかと思ったのですが、そんなに甘くはありませんでした。
ただこの仕様、複合主キーテーブルに対してUpsertするときすごく便利です。
イマドキ複合主キー?と思われそうですが、仕事で複合主キーのテーブルを取り扱う機会がすごく多いのでupsert()は救世主でした。
(今まではクエリビルダー使ったり自前でメソッド作ったりしてました)
第三引数について
第三引数にはupdate時更新したいカラムを配列で指定します。先ほどの例だとpriceカラムのみ更新します。
未指定の場合は第一引数で指定したすべてのデータを更新するようです。Modelのguardedで更新しないカラムを指定していれば除外してくれるのかな?(未確認)
ただし 空の配列を指定した場合は更新日時カラム(updated_at)のみ更新します。
なので第一引数にいくら値を指定しても更新されません。
$data = [
['id' => 1,'departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['id' => 2,'departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
]
//$dataに含まれるModelのguardedで指定したカラム以外を更新
Flight::upsert($data, ['id']);
//updated_atのみ更新。$dataで指定したdepartureやpriceは更新されない
Flight::upsert($data, ['id'],[]);
参考
https://readouble.com/laravel/8.x/ja/eloquent.html#upserts
https://stackoverflow.com/questions/66198271/laravel-8-eloquent-upsert-inserting-new-records