Laravel Magazine
firstOrCreate vs updateOrCreate vs firstOrNew: Picking the Right Eloquent Shortcut

firstOrCreate vs updateOrCreate vs firstOrNew: Picking the Right Eloquent Shortcut

Eric Van Johnson ·

Eloquent gives you three methods that all sound like they do the same thing: find a record, and if it's not there, make one. Once you actually need to pick between them, though, the subtle differences matter a lot more than the names suggest.

The three shortcuts

// 1. firstOrCreate — find it, or create and save it
$subscriber = Subscriber::firstOrCreate(
    ['email' => 'dev@phparchitect.com'],
    ['name' => 'PHP Architect', 'plan' => 'pro']
);

// 2. updateOrCreate — find it, update it, or create and save it
$subscriber = Subscriber::updateOrCreate(
    ['email' => 'dev@phparchitect.com'],
    ['plan' => 'pro']
);

// 3. firstOrNew — find it, or build it WITHOUT saving
$subscriber = Subscriber::firstOrNew(
    ['email' => 'dev@phparchitect.com'],
    ['name' => 'PHP Architect']
);

All three take the same two arguments: an array of attributes to search by, and an array of attributes to fill in on the new (or updated) record. The difference is entirely in what happens after the lookup.

firstOrCreate: leaves existing rows alone

If a matching row exists, firstOrCreate returns it untouched — the second argument is only used when a new row has to be created. This makes it perfect for "get or register" scenarios where you never want to silently overwrite existing data:

$category = Category::firstOrCreate(['slug' => 'laravel-tips']);

If that category already exists, nothing changes. If it doesn't, it's created and saved in one call.

updateOrCreate: always applies the second array

This is the one that trips people up. updateOrCreate applies the attributes in the second array whether the record is new or already existed:

$page = Page::updateOrCreate(
    ['path' => '/pricing'],
    ['views' => $currentViews + 1]
);

Every time this runs, views gets written — even on a row that already existed. That's usually exactly what you want for things like tracking a "last synced at" timestamp or upserting data from an external API. But if you only meant to set defaults on first creation, updateOrCreate is the wrong tool; reach for firstOrCreate instead.

firstOrNew: no database write at all

firstOrNew looks and feels the same as firstOrCreate, but it never touches the database. It returns a model instance — existing or freshly instantiated — that you still have to save() yourself:

$invoice = Invoice::firstOrNew(['number' => $number]);
$invoice->amount_due = $amountDue;
$invoice->save();

This is the right choice when you need to inspect or modify the model before deciding whether it's worth persisting, or when the save needs to happen inside a larger transaction alongside other writes.

Watch the race condition

None of these three methods are atomic on their own. Between the SELECT and the INSERT, another request can create a matching row, and you'll get a duplicate-key exception instead of the record you expected. If the search columns have a unique constraint and the table sees concurrent writes, wrap the call:

try {
    $subscriber = Subscriber::firstOrCreate(['email' => $email]);
} catch (\Illuminate\Database\UniqueConstraintViolationException) {
    $subscriber = Subscriber::where('email', $email)->first();
}

Or, if the table is contended enough to matter, take a lock first with lockForUpdate() inside a DB::transaction() closure.

The quick rule of thumb

  • Use firstOrCreate when new attributes should only apply on creation.
  • Use updateOrCreate when the second array should always be written, new row or not.
  • Use firstOrNew when you need a model instance to inspect or modify before deciding to save.

Same shape, three very different behaviors — pick based on what should happen to a row that already exists, not just on habit.

Stay Updated

Subscribe to our newsletter

Get latest news, tutorials, community articles and podcast episodes delivered to your inbox.

Weekly articles
We send a new issue of the newsletter every week on Friday.
No spam
We'll never share your email address and you can opt out at any time.