LaravelのupdateOrCreateは、指定した条件のレコードが存在すれば更新し、存在しなければ新規作成する便利なEloquentメソッドです。この記事では、実際にコードを実行して確認した動作をもとに、基本的な使い方からfirstOrCreate・upsert()との違い、リレーション経由での使用例、注意すべきポイントまで解説します。
updateOrCreateの早見表
| メソッド | レコードが無い場合 | レコードが有る場合 | 対象件数 | 戻り値 |
|---|---|---|---|---|
updateOrCreate() |
作成する | 更新する | 1件 | モデルインスタンス |
firstOrCreate() |
作成する | そのまま返す(更新しない) | 1件 | モデルインスタンス |
firstOrNew() |
未保存のインスタンスを生成 | そのまま返す | 1件 | モデルインスタンス(未保存の場合あり) |
upsert() |
作成する | 更新する | 複数件を1クエリで一括処理 | 影響行数(int) |
updateOrCreateの基本的な使い方
updateOrCreate()は第1引数に「検索条件」、第2引数に「作成・更新する値」を配列で渡します。検索条件に一致するレコードがあれば第2引数の内容で更新し、無ければ検索条件と値をマージして新規作成します。
$user = User::updateOrCreate(
['email' => 'user@example.com'],
['name' => 'John Doe', 'age' => 30]
);
実際にローカル環境(Laravel 13.12.0、SQLite)で動作を確認したところ、次のようになりました。
// 1回目: レコードが無いので新規作成される
$u1 = User::updateOrCreate(['email' => 'a@example.com'], ['name' => 'Alice', 'age' => 20]);
$u1->wasRecentlyCreated; // true
User::count(); // 1
// 2回目: 同じemailで呼ぶと、既存レコードが更新される(新規作成されない)
$u2 = User::updateOrCreate(['email' => 'a@example.com'], ['name' => 'Alice Updated', 'age' => 21]);
$u2->wasRecentlyCreated; // false
User::count(); // 1(変わらない)
$u1->id === $u2->id; // true(同一レコード)
$u2->name; // "Alice Updated"
ポイントは、第1引数(検索条件)で指定したカラムは新規作成時にそのまま保存され、第2引数(値)で指定したカラムは新規作成・更新のどちらでも上書きされるという点です。両方に同じキーを含めても問題ありません。
戻り値とwasRecentlyCreatedで作成/更新を判定する
updateOrCreate()の戻り値は常にモデルインスタンスです。作成されたか更新されたかを処理内で分岐したい場合は、モデルのwasRecentlyCreatedプロパティを確認します。
$product = Product::updateOrCreate(
['sku' => $sku],
['price' => $price, 'stock' => $stock]
);
if ($product->wasRecentlyCreated) {
Log::info("新規商品を登録しました: {$sku}");
} else {
Log::info("既存商品を更新しました: {$sku}");
}
リレーション経由でupdateOrCreateを使う
updateOrCreate()はリレーションのクエリビルダからも呼び出せます。この場合、外部キーは自動的に検索条件・作成データへ含まれるため、明示的に指定する必要がありません。実際にhasManyリレーションで検証すると、次のようにauthor_idが自動でセットされました。
class Author extends Model
{
public function books()
{
return $this->hasMany(Book::class);
}
}
$author = Author::find(1);
// author_id は指定しなくても自動的に $author->id がセットされる
$book = $author->books()->updateOrCreate(
['isbn' => '978-4-00-000000-0'],
['title' => 'こころ', 'stock' => 5]
);
$book->author_id === $author->id; // true
注意点として、検索条件に指定していないカラム(上記の例ではtitle)は一致判定に使われません。同じisbnでtitleを変えて呼び出すと、タイトルは新しい値で上書きされるだけで別レコードにはなりません。逆に、検索条件に含めたカラムの値を変えると別レコードとして新規作成されるため、「何を条件にレコードを同一視するか」を最初に設計しておくことが重要です。
updateOrCreateとfirstOrCreate・firstOrNewの違い
updateOrCreate()と混同されやすいメソッドにfirstOrCreate()とfirstOrNew()があります。いずれも第1引数に検索条件を渡す点は共通していますが、レコードが存在した場合の挙動が異なります。
firstOrCreate():レコードがあればそのまま返す。第2引数の値で更新はしないfirstOrNew():レコードが無い場合、保存されていないインスタンスを返す(呼び出し側でsave()が必要)updateOrCreate():レコードがあれば第2引数の値で更新して保存する
「既存データがあれば内容を最新化したい」場合はupdateOrCreate()、「無ければ作るだけで、既存データには触れたくない」場合はfirstOrCreate()を選びます。
大量データを扱う場合はupsert()も検討する
updateOrCreate()は1回の呼び出しにつきSELECTとINSERT/UPDATEが発生するため、ループの中で大量に呼び出すとレコード数だけクエリが発生し、パフォーマンスの問題になりやすい方法です。複数件をまとめて登録・更新したい場合は、クエリビルダのupsert()を使うと1回のクエリで処理できます。
// 複数件をまとめてupsert(第2引数: 一意判定に使うカラム、第3引数: 重複時に更新するカラム)
Item::upsert(
[
['sku' => 'A1', 'stock' => 10],
['sku' => 'A2', 'stock' => 5],
],
['sku'],
['stock']
);
実際に検証したところ、upsert()は指定した更新カラムにupdated_atを含めなくても、タイムスタンプ付きモデルであれば自動的にupdated_atが更新されました。ただしupsert()はEloquentのcreating/updatingなどのモデルイベントを発火しない点に注意してください。イベントを使った処理(通知やログなど)が必要な場合は、件数が少なければupdateOrCreate()、多い場合はイベント処理を別途用意した上でのupsert()検討が現実的です。
updateOrCreateを使う際の注意点
検索条件は完全一致で判定される
検索条件に指定した配列はwhere()と同様に扱われ、部分一致や曖昧な判定はできません。「同一レコードとみなす条件」を過不足なく指定することが大切です。
一意制約(unique制約)を設定しておく
Laravelの内部実装を確認すると、updateOrCreate()はfirstOrCreate()を経由し、レコード作成時に一意制約違反(UniqueConstraintViolationException)が発生した場合は自動的に該当レコードを再取得する仕組みになっています。つまり、検索条件に使うカラムにデータベース側のunique制約が設定されていれば、同時アクセスによる重複作成もある程度安全に扱われます。逆に言えば、アプリケーション側の条件指定だけに頼り、DB側にunique制約が無い場合は、同時実行時に重複レコードが作成される可能性が残るため、マイグレーションで$table->unique()を設定しておくことをおすすめします。
よくある質問(FAQ)
updateOrCreateとは?
指定した条件に一致するレコードがあれば値を更新し、無ければ条件と値をマージして新規作成するEloquentのメソッドです。「レコードの有無を確認してから作成/更新を出し分ける」処理を1行で書けます。
updateOrCreateとfirstOrCreateの使い分けは?
既存データの内容を最新化したいならupdateOrCreate()、既存データがあればそれを変更せずそのまま使いたいならfirstOrCreate()を使います。
updateOrCreateで大量データを処理すると遅くなりますか?
1件ごとにSELECTとINSERT/UPDATEが発生するため、件数が多いループ処理では遅くなりがちです。大量データの一括登録・更新にはupsert()の利用を検討してください。
updateOrCreateはトランザクション内で使えますか?
使えます。他の更新処理と合わせてDB::transaction()で包むことで、一連の操作を原子的に行えます。
まとめ
LaravelのupdateOrCreate()は、レコードの有無を判定してから作成・更新を出し分ける処理を簡潔に書けるメソッドです。実際に検証した結果をもとに整理すると、次の点を押さえておくと安全に使えます。
- 戻り値は常にモデルインスタンスで、
wasRecentlyCreatedで作成/更新を判定できる - リレーション経由で呼び出すと外部キーが自動セットされる
- 既存データを変更したくない場合は
firstOrCreate()を使う - 大量データの一括処理には
upsert()を検討する - 検索条件のカラムには一意制約を設定し、重複作成を防ぐ
データ更新の基本パターンについては「Laravel Eloquentでデータ更新を効果的に行う方法とベストプラクティス」、hasManyリレーションの基本は「LaravelのHasManyリレーションを使いこなす方法とベストプラクティスガイド」もあわせて参考にしてください。Eloquentの基本操作を一から確認したい方は「Laravel Eloquentとは?使い方の基本からリレーション・クエリビルダとの違いまで徹底解説」もご覧ください。

コメント