Mass upsert in Laravel (Eloquent)

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Mass Upsert in Laravel (Eloquent): The Efficient Way to Handle Inserts and Updates As senior developers working with Laravel and relational databases like MySQL, we constantly run into scenarios where we need to synchronize data efficiently. One of the most common needs is the **mass upsert**—inserting new records or updating existing ones based on a set of input data, all in a single, highly optimized database operation. The question is: Is there a native, elegant way to perform a mass upsert directly through Eloquent? The short answer is no, not directly via standard Eloquent methods alone. However, by understanding the underlying power of MySQL and leveraging Laravel's Query Builder or raw expressions, we can achieve this efficiently. This post will dive into why standard Eloquent falls short for this task and demonstrate the most performant way to execute mass upserts in a Laravel application against a MySQL database. ## Why Standard Eloquent Falls Short Eloquent is fantastic for CRUD (Create, Read, Update, Delete) operations involving single models or simple relationships. Methods like `create()` handle inserts, and `update()` handles updates perfectly fine. When you attempt to perform a mass upsert using standard Eloquent methods within a loop—for example, iterating over an array and calling `create()` or `updateOrCreate()` for each item—you introduce significant overhead. Each call results in a separate database round trip, which is extremely slow when dealing with hundreds or thousands of records. For true *mass* operations, we must bypass the ORM layer temporarily and speak directly to the database engine's capabilities, specifically MySQL’s powerful `INSERT ... ON DUPLICATE KEY UPDATE` syntax. This command allows the database to handle the conditional logic atomically and at high speed, minimizing latency. ## The Optimal Solution: Leveraging MySQL's Power The most efficient method for mass upserts in a MySQL environment is to construct a single SQL statement that incorporates the "upsert" logic directly into the query. Laravel’s Query Builder provides the necessary tools to execute this raw, optimized command. ### Step-by-Step Implementation Let's assume we have a `products` table with a unique index on the `id` column (which acts as our duplicate key). We want to process the following data: ```php [ ['id' => 1, 'value' => 100], // Update existing record 1 ['id' => 3, 'value' => 20] // Insert new record 3 ] ``` We can use the `upsert` method available on the Query Builder (or raw expressions) to handle this complexity. While Eloquent doesn't have a dedicated `massUpsert()` method natively across all models, we achieve this by executing the required SQL structure. Here is how you structure the query using the `upsert` functionality: ```php use Illuminate\Support\Facades\DB; use App\Models\Product; // Assuming your model is named Product $dataToUpsert = [ ['id' => 1, 'value' => 100], ['id' => 3, 'value' => 20] ]; // We need to ensure the structure matches what MySQL expects for ON DUPLICATE KEY UPDATE. // Note: The exact syntax often requires careful mapping depending on your primary key setup. DB::table('products')->upsert( $dataToUpsert, ['id'], // Columns to check for duplicates (the unique index) ['value' => DB::raw('VALUES(value)')] // What to update if a duplicate is found ); ``` ### Explanation of the Code 1. **`DB::table('products')->upsert(...)`**: We initiate the operation on the specific table via the Query Builder, bypassing Eloquent for this highly optimized bulk task. 2. **First Argument (`$dataToUpsert`)**: This is the array of new or existing rows we want to process. 3. **Second Argument (`['id']`)**: This tells MySQL which column(s) define the uniqueness constraint (the duplicate key). If a row with `id=1` already exists, MySQL knows where to apply the update logic. 4. **Third Argument (`['value' => DB::raw('VALUES(value)')]`)**: This defines the update action. We instruct the database: "If a conflict occurs (i.e., ID exists), update the `value` column with the value provided in the `VALUES(value)` clause of the incoming row." This single command achieves both insertion and updating simultaneously, making it vastly superior in performance compared to looping through Eloquent methods. For deeper dives into database interactions within Laravel, understanding these underlying mechanisms—as emphasized by resources like those found on the official **laravelcompany.com** documentation—is crucial for building high-performance applications. ## Conclusion While Eloquent provides a beautiful abstraction layer for standard CRUD operations, complex mass synchronization tasks like upserts demand a deeper engagement with the database. By recognizing the limitations of ORM overhead and utilizing native MySQL features like `INSERT ... ON DUPLICATE KEY UPDATE` via the Query Builder, developers can achieve lightning-fast performance for bulk data manipulation. Always choose the most efficient tool for the job; in this case, direct SQL optimization is king for mass operations.