【Laravel】EloquentのfirstOrCreate・firstOrNew・updateOrCreate の違いと使い分け
LaravelのEloquentには、「レコードを取得しつつ、なければ作成する」という処理を簡潔に書けるメソッドが複数あります。
「firstOrCreate と updateOrCreate の違いがわからない」
「firstOrNew はいつ使うの?」
「取得したレコードが新規か既存かを判定したい」
この記事では、3つのメソッドの違いと実務での使い分けを、具体的なコード例とともに解説します。
1. 3つのメソッドの違い(比較表)
| メソッド | レコードが存在する場合 | レコードが存在しない場合 | DBに保存 |
|---|---|---|---|
firstOrCreate | 既存レコードを返す | 新規作成して返す | ✅ する |
firstOrNew | 既存レコードを返す | 新規インスタンスを返す | ❌ しない |
updateOrCreate | 既存レコードを更新して返す | 新規作成して返す | ✅ する |
一言でまとめると:
firstOrCreate→ 取得するか、なければ作る(更新はしない)firstOrNew→ 取得するか、なければインスタンスだけ作る(保存は自分でする)updateOrCreate→ 取得して更新するか、なければ作る
2. firstOrCreate の使い方
firstOrCreate は第1引数の条件でレコードを検索し、見つかれば返します。見つからなければ第1引数と第2引数をマージした値で新規レコードを作成して返します。
// 基本的な使い方
$user = User::firstOrCreate(
['email' => 'test@example.com'], // 検索条件
);
// 第2引数で作成時のみ使う値を指定
$user = User::firstOrCreate(
['email' => 'test@example.com'], // 検索条件
['name' => 'Test User', 'role' => 'member'] // 作成時のみ使う値
);
wasRecentlyCreated で新規か既存かを判別する
firstOrCreate の戻り値には wasRecentlyCreated プロパティがあり、新規作成されたかどうかを確認できます。
$user = User::firstOrCreate(
['email' => 'test@example.com'],
['name' => 'Test User']
);
if ($user->wasRecentlyCreated) {
// 新規作成された場合の処理
Mail::to($user)->send(new WelcomeMail($user));
} else {
// 既存レコードが返された場合の処理
Log::info('既存ユーザーがログインしました', ['id' => $user->id]);
}
実務での使用例
// OAuth認証でユーザーを取得または作成
$user = User::firstOrCreate(
['provider' => 'google', 'provider_id' => $googleUser->id],
[
'name' => $googleUser->name,
'email' => $googleUser->email,
]
);
// タグの取得または作成
$tag = Tag::firstOrCreate(['name' => $tagName]);
// 設定値の取得または作成(デフォルト値付き)
$setting = UserSetting::firstOrCreate(
['user_id' => $userId, 'key' => 'theme'],
['value' => 'light'] // デフォルト値
);
3. firstOrNew の使い方
firstOrNew は firstOrCreate とほぼ同じですが、DBへの保存を行いません。インスタンスを返すだけなので、保存前に追加の処理をしたい場合に使います。
$user = User::firstOrNew(
['email' => 'test@example.com'],
['name' => 'Test User']
);
// この時点ではDBに保存されていない(新規の場合)
// $user->exists で既存かどうかを確認できる
if (!$user->exists) {
// 追加の処理
$user->activation_token = Str::random(32);
$user->save(); // ここで保存
}
firstOrCreate との使い分け
// firstOrCreate:取得 or 即時作成(追加処理不要な場合)
$tag = Tag::firstOrCreate(['name' => $tagName]);
// firstOrNew:取得 or インスタンス生成(保存前に追加処理が必要な場合)
$profile = UserProfile::firstOrNew(['user_id' => $userId]);
$profile->last_login_at = now();
$profile->save();
実務での使用例
// バリデーション後に保存する場合
$address = Address::firstOrNew(['user_id' => $userId]);
$address->fill($validatedData);
if ($address->isDirty()) {
$address->save();
event(new AddressUpdated($address));
}
4. updateOrCreate の使い方
updateOrCreate は第1引数の条件でレコードを検索し、見つかれば第2引数で更新します。見つからなければ第1引数と第2引数をマージして新規作成します。
// 基本的な使い方
$user = User::updateOrCreate(
['email' => 'test@example.com'], // 検索条件
['name' => 'Updated Name', 'role' => 'admin'] // 更新または作成する値
);
firstOrCreate との違い
$email = 'test@example.com';
// firstOrCreate:既存レコードは更新しない
$user = User::firstOrCreate(
['email' => $email],
['name' => 'New Name']
);
// → 既存レコードがあれば name は変わらない
// updateOrCreate:既存レコードも更新する
$user = User::updateOrCreate(
['email' => $email],
['name' => 'New Name']
);
// → 既存レコードがあれば name が 'New Name' に更新される
実務での使用例
// 外部APIのデータを同期する(upsert的な処理)
foreach ($apiResponse['users'] as $userData) {
User::updateOrCreate(
['external_id' => $userData['id']],
[
'name' => $userData['name'],
'email' => $userData['email'],
'updated_at' => now(),
]
);
}
// ユーザー設定の保存(存在すれば更新、なければ作成)
UserSetting::updateOrCreate(
['user_id' => $userId, 'key' => 'notification_enabled'],
['value' => $enabled ? '1' : '0']
);
// プロフィールの更新または初期作成
UserProfile::updateOrCreate(
['user_id' => auth()->id()],
$request->validated()
);
5. 実務での使い分け基準
30年のLaravel実務から整理した判断基準です。
既存レコードを更新したくない場合 → firstOrCreate
例:タグの取得・作成、OAuth認証のユーザー作成
保存前に追加処理が必要な場合 → firstOrNew
例:トークン生成、複数テーブルへの紐付け処理
常に最新の値で上書きしたい場合 → updateOrCreate
例:外部APIとのデータ同期、設定値の保存
まとめ
| やりたいこと | 使うメソッド |
|---|---|
| なければ作る(あっても更新しない) | firstOrCreate |
| なければインスタンス生成(保存は自分で) | firstOrNew |
| あれば更新、なければ作る | updateOrCreate |
| 新規か既存かを判定したい | wasRecentlyCreated プロパティ |
| 既存かどうかを確認したい(firstOrNew後) | $model->exists プロパティ |
迷ったときの判断軸は「既存レコードを更新したいかどうか」です。更新したいなら updateOrCreate、したくないなら firstOrCreate を選べばほぼ間違いありません。



