How to Use Laravel Update or Create: The Efficient Upsert Pattern for Eloquent Models

Use the updateOrCreate method on Eloquent models to atomically update existing records or insert new ones with minimal database queries and built-in race condition handling.

When building applications with the laravel/framework repository, you frequently encounter scenarios where you need to either update an existing database record or create a new one if it does not exist. This laravel update or create pattern, commonly known as an "upsert," is implemented efficiently through Eloquent's dedicated builder methods that optimize query count and ensure transactional safety.

How Laravel Update or Create Works Under the Hood

The updateOrCreate method is implemented in src/Illuminate/Database/Eloquent/Builder.php and provides a robust, race-condition-safe mechanism for handling upsert operations.

The Single-Step API

The method signature updateOrCreate(array $attributes, array $values = []) accepts two parameters:

  • $attributes: The criteria used to search for an existing record (typically unique keys like email or UUID)
  • $values: The attributes to update or set on the found or newly created model

Internally, the method delegates to firstOrCreate, which executes a SELECT query to locate the record. If found, the model is returned immediately. If not found, the system attempts an INSERT operation.

Transactional Safety and Race Condition Handling

The implementation uses database save-points via withSavepointIfNeeded to ensure atomicity. When two concurrent requests attempt to insert the same unique key simultaneously:

  1. The first request successfully inserts the record
  2. The second request catches the UniqueConstraintViolationException
  3. The second request then fetches the existing record that was just created by the first request

This guarantees that only one INSERT is ever performed per unique key, even under heavy contention, while ensuring both requests receive a valid model instance.

Query Efficiency

When updating an existing record, the method calls fill() with the $values array and then save(). Because the model is already loaded from the database, this issues only a single UPDATE query for the changed fields. The entire operation typically requires either:

  • One SELECT + one UPDATE (existing record), or
  • One SELECT + one INSERT (new record)

Laravel Update or Create Examples

Basic Usage

The most common pattern matches a unique identifier and updates specific fields while creating the record if it does not exist:

use App\Models\User;

// Update the user if the email exists, otherwise create a new record
$user = User::query()->updateOrCreate(
    ['email' => 'jane@example.com'],              // Search attributes (unique key)
    ['name' => 'Jane Doe', 'is_active' => true]  // Values to set or update
);

Dynamic Values with Closures

For complex logic or computed values, pass a closure as the second argument:

$user = User::query()->updateOrCreate(
    ['email' => $request->email],
    fn () => [
        'name' => $request->name,
        'last_login_at' => now(),
        'login_count' => DB::raw('login_count + 1'),
    ]
);

Relationship Operations

Eloquent relationships also expose the updateOrCreate method. For a HasMany relationship (e.g., a user has many phones):

// UpdateOrCreate on a HasMany relationship
$user->phones()->updateOrCreate(
    ['type' => 'mobile'],
    ['number' => $request->mobile_number, 'verified_at' => now()]
);

For BelongsToMany relationships, the method handles pivot data automatically through the implementation in src/Illuminate/Database/Eloquent/Relations/BelongsToMany.php.

Bulk Operations with Upsert

When processing multiple records simultaneously, use the lower-level upsert method to minimize query count:

// Bulk upsert for large batches
User::upsert(
    [
        ['email' => 'john@example.com', 'name' => 'John Smith'],
        ['email' => 'jane@example.com', 'name' => 'Jane Doe'],
        ['email' => 'bob@example.com', 'name' => 'Bob Wilson'],
    ],
    ['email'],              // Unique key(s) to match
    ['name']                // Columns to update if match found
);

The upsert method is available directly on the model and is distinct from updateOrCreate, which is designed for single-row operations.

Key Implementation Files in Laravel Framework

The laravel update or create functionality is implemented across several core files in the laravel/framework repository:

Summary

  • Use updateOrCreate for single-row upsert operations where you need to either update an existing record or create a new one based on unique attributes.
  • The method is race-condition safe through the use of database save-points and exception handling for unique constraint violations.
  • It minimizes queries by issuing either a SELECT + UPDATE or SELECT + INSERT, never more than two queries per operation.
  • For bulk operations, use the upsert method instead to process multiple records in a single query.
  • Relationship methods in HasOneOrMany.php and BelongsToMany.php provide the same atomic guarantees for related models.

Frequently Asked Questions

What is the difference between updateOrCreate and firstOrCreate in Laravel?

firstOrCreate attempts to find a record matching the given attributes and creates it only if no match is found, using the same attributes for both searching and creation. updateOrCreate separates the search criteria from the update values, allowing you to match on specific keys (like email) while updating different fields (like name or timestamps). According to the source code in Builder.php, updateOrCreate actually delegates to firstOrCreate internally but adds the additional step of filling and saving the model with the separate $values array.

How does Laravel handle race conditions in updateOrCreate?

Laravel handles race conditions through a combination of database save-points and exception handling implemented in src/Illuminate/Database/Eloquent/Builder.php. When two concurrent requests attempt to insert the same unique key simultaneously, the second request catches the UniqueConstraintViolationException, rolls back only the save-point (leaving outer transactions intact), and then fetches the existing record that was created by the first request. This ensures that only one INSERT occurs while both requests receive a valid model instance.

Is updateOrCreate atomic in Laravel?

Yes, updateOrCreate is atomic when used with database transactions. The method uses withSavepointIfNeeded to create a database save-point around the INSERT operation. If the INSERT fails due to a unique constraint violation, only the save-point is rolled back, not the entire transaction. This allows the operation to safely fall back to selecting the existing record without affecting surrounding database operations. However, the SELECT and UPDATE/INSERT combination itself is not a single SQL statement—it's two queries wrapped in transactional logic.

When should I use upsert instead of updateOrCreate?

Use the upsert method instead of updateOrCreate when you need to process multiple records in a single operation to minimize database queries. While updateOrCreate is optimized for single-row operations (requiring up to two queries per row), upsert can insert or update many rows with a single query by leveraging the database's native upsert capabilities. According to the Laravel source, upsert is available directly on the model for bulk operations, whereas updateOrCreate is the idiomatic choice when dealing with individual model instances or relationship operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →