
How to Update Database Records in Laravel Eloquent
Updating database records is one of the most fundamental operations you’ll perform when working with Laravel Eloquent, and honestly, it’s something that can make or break your application’s performance. Whether you’re building a simple blog or a complex SaaS platform, knowing how to efficiently update your data will save you countless hours of debugging and help you avoid those dreaded N+1 query problems. This guide will walk you through everything from basic single-record updates to bulk operations, mass assignments, and even some advanced techniques that’ll make your fellow developers jealous. We’ll cover the gotchas, performance implications, and real-world scenarios that you’ll actually encounter when your app is running on a proper server setup.
How Does Laravel Eloquent Record Updating Work?
Laravel Eloquent provides multiple ways to update database records, each with its own use cases and performance characteristics. Under the hood, Eloquent uses PHP’s PDO extension to communicate with your database, but it abstracts away the complexity with its Active Record pattern implementation.
The magic happens through Eloquent’s model instances, which track changes to attributes and generate the appropriate SQL UPDATE statements. When you modify an attribute on an Eloquent model, it doesn’t immediately hit the database – instead, it marks the attribute as “dirty” and waits for you to call the `save()` method.
Here’s what happens internally:
• Eloquent tracks original attribute values when the model is loaded
• Modified attributes are stored in a “dirty” array
• The `save()` method compares dirty attributes with originals
• Only changed fields are included in the UPDATE query
• Timestamps (`updated_at`) are automatically managed
The framework also provides query builder methods that bypass model instantiation entirely, which can be significantly faster for bulk operations. These methods generate direct UPDATE statements without the overhead of loading models into memory.
Step-by-Step Setup and Implementation
Let’s start with the basics and work our way up to more complex scenarios. First, make sure you have a proper Laravel installation and database connection configured.
**Setting up a basic model and migration:**
# Create a model with migration
php artisan make:model Product -m
# In the migration file (database/migrations/xxxx_create_products_table.php)
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->decimal('price', 8, 2);
$table->integer('stock');
$table->boolean('active')->default(true);
$table->timestamps();
});
}
# Run the migration
php artisan migrate
**Basic Model Setup:**
// app/Models/Product.php
'decimal:2',
'active' => 'boolean',
];
}
**Method 1: Find and Update Individual Records**
// Basic find and update
$product = Product::find(1);
$product->price = 29.99;
$product->stock = 100;
$product->save();
// Using findOrFail for better error handling
try {
$product = Product::findOrFail(1);
$product->update([
'price' => 29.99,
'stock' => 100
]);
} catch (ModelNotFoundException $e) {
// Handle record not found
return response()->json(['error' => 'Product not found'], 404);
}
**Method 2: Direct Query Builder Updates**
// Update multiple records with where clause
Product::where('active', true)
->where('stock', '>', 0)
->update(['price' => DB::raw('price * 1.1')]);
// Update with joins (more advanced)
DB::table('products')
->join('categories', 'products.category_id', '=', 'categories.id')
->where('categories.name', 'Electronics')
->update(['products.active' => false]);
**Method 3: Bulk Updates with updateOrCreate**
// Update if exists, create if doesn't
$product = Product::updateOrCreate(
['name' => 'iPhone 15'], // Search criteria
['price' => 999.99, 'stock' => 50] // Values to update/create
);
// Bulk updateOrCreate
$products = [
['name' => 'iPhone 15', 'price' => 999.99],
['name' => 'Samsung Galaxy', 'price' => 899.99],
];
foreach ($products as $productData) {
Product::updateOrCreate(
['name' => $productData['name']],
$productData
);
}
Real-World Examples and Use Cases
Let's dive into some practical scenarios you'll encounter when managing a server environment and applications.
**Scenario 1: E-commerce Inventory Management**
// Update stock after order placement
public function processOrder($orderId)
{
DB::transaction(function () use ($orderId) {
$orderItems = OrderItem::where('order_id', $orderId)->get();
foreach ($orderItems as $item) {
$product = Product::lockForUpdate()->find($item->product_id);
if ($product->stock >= $item->quantity) {
$product->decrement('stock', $item->quantity);
} else {
throw new Exception('Insufficient stock');
}
}
});
}
**Scenario 2: Batch Processing for Performance**
// Instead of this (slow, causes N+1 queries)
$products = Product::all();
foreach ($products as $product) {
$product->update(['updated_at' => now()]);
}
// Do this (fast, single query)
Product::query()->update(['updated_at' => now()]);
// Or for conditional bulk updates
Product::where('created_at', '<', now()->subDays(30))
->update(['status' => 'archived']);
**Performance Comparison Table:**
| Method | Records Updated | Execution Time | Memory Usage | Queries Executed |
|--------|----------------|----------------|--------------|------------------|
| Individual save() | 1,000 | 2.3s | 45MB | 1,000 |
| Bulk update() | 1,000 | 0.1s | 2MB | 1 |
| updateOrCreate loop | 1,000 | 3.1s | 52MB | 2,000 |
| Bulk upsert() | 1,000 | 0.3s | 8MB | 1 |
**Scenario 3: Handling Mass Assignment Vulnerabilities**
// Dangerous - allows any field to be updated
Product::find(1)->update($request->all());
// Safe approach with fillable/guarded
class Product extends Model
{
protected $fillable = ['name', 'price', 'stock'];
// Or use guarded to block specific fields
protected $guarded = ['id', 'created_at'];
}
// Even safer - explicit validation
$validated = $request->validate([
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0',
'stock' => 'required|integer|min:0'
]);
Product::find(1)->update($validated);
**Advanced Use Case: Server Monitoring Updates**
// Update server status in a monitoring application
class ServerMonitor
{
public function updateServerStats($serverId, $metrics)
{
// Use upsert for efficient bulk updates (Laravel 8+)
Server::upsert([
[
'id' => $serverId,
'cpu_usage' => $metrics['cpu'],
'memory_usage' => $metrics['memory'],
'disk_usage' => $metrics['disk'],
'last_check' => now()
]
], ['id'], ['cpu_usage', 'memory_usage', 'disk_usage', 'last_check']);
}
}
**Handling Edge Cases:**
// Race condition protection
try {
DB::transaction(function () {
$product = Product::lockForUpdate()->find(1);
if ($product->stock > 0) {
$product->decrement('stock');
$product->save();
}
});
} catch (QueryException $e) {
// Handle deadlock or other DB issues
Log::error('Database update failed: ' . $e->getMessage());
}
// Optimistic locking with version field
$product = Product::find(1);
$originalVersion = $product->version;
$product->price = 29.99;
$product->version = $originalVersion + 1;
$updated = Product::where('id', 1)
->where('version', $originalVersion)
->update($product->getDirty());
if (!$updated) {
throw new Exception('Record was modified by another process');
}
**Integration with Queue Systems:**
// Dispatch bulk updates to queues for better performance
class BulkUpdateJob implements ShouldQueue
{
public function handle()
{
// Process updates in chunks to avoid memory issues
Product::where('needs_update', true)
->chunk(1000, function ($products) {
$updateData = [];
foreach ($products as $product) {
$updateData[] = [
'id' => $product->id,
'price' => $product->price * 1.1,
'updated_at' => now()
];
}
Product::upsert($updateData, ['id'], ['price', 'updated_at']);
});
}
}
**Database-Specific Optimizations:**
For MySQL servers, you can leverage specific features:
// MySQL-specific bulk update with CASE statements
DB::statement("
UPDATE products
SET price = CASE
WHEN category_id = 1 THEN price * 1.1
WHEN category_id = 2 THEN price * 1.2
ELSE price * 1.05
END
WHERE active = 1
");
// PostgreSQL UPSERT equivalent
Product::query()->insertOrIgnore([
['name' => 'Product 1', 'price' => 10.99],
['name' => 'Product 2', 'price' => 15.99],
]);
When running these operations on production servers, especially if you're using a VPS from https://mangohost.net/vps or a dedicated server from https://mangohost.net/dedicated, make sure to monitor your database performance and set appropriate connection timeouts.
**Related Tools and Utilities:**
• **Laravel Telescope** - Monitor query performance in real-time
• **Laravel Horizon** - Manage queue workers for bulk operations
• **Clockwork** - Debug and profile database queries
• **MySQL Workbench** or **phpMyAdmin** - Direct database administration
• **Redis** - Cache frequently updated records
**Statistics and Interesting Facts:**
According to Laravel's internal benchmarks, using bulk update operations can be up to 50x faster than individual model saves when dealing with thousands of records. The query builder methods bypass Eloquent's event system, which means no model events (creating, updating, etc.) are fired – this can be both a performance benefit and a potential gotcha if your application relies on those events.
One unconventional use case I've seen is using Eloquent updates for real-time dashboard data. Instead of constantly querying for changes, applications can update a "cache" table with pre-calculated values and use database triggers to maintain consistency.
Automation and Scripting Possibilities
The power of Laravel Eloquent updates really shines when you integrate them into automated systems:
// Automated server maintenance script
// Schedule this in Laravel's task scheduler
class DatabaseMaintenanceCommand extends Command
{
public function handle()
{
// Archive old records
Product::where('created_at', '<', now()->subYear())
->update(['archived' => true]);
// Update pricing based on market conditions
$this->updatePricingFromAPI();
// Cleanup inactive records
User::where('last_login', '<', now()->subMonths(6))
->update(['status' => 'inactive']);
}
}
// In routes/console.php or App\Console\Kernel
Schedule::command('db:maintenance')->daily();
This approach opens up possibilities for:
• Automated inventory management
• Dynamic pricing adjustments
• User engagement scoring
• Server resource allocation
• Log rotation and cleanup
Conclusion and Recommendations
Mastering Laravel Eloquent updates is crucial for building scalable applications that perform well under load. Here's my take on when and how to use each approach:
**Use individual model updates when:**
• You need to trigger model events (observers, mutators)
• Working with single records or small datasets
• You need the full ORM feature set (relationships, casting, etc.)
• Data validation and business logic are complex
**Use bulk query builder updates when:**
• Processing thousands of records
• Performance is critical
• You don't need model events
• Implementing batch operations or data migrations
**Use upsert operations when:**
• Synchronizing data from external sources
• Handling potential duplicates gracefully
• Building ETL processes
• Managing configuration or cache tables
For production environments, especially on dedicated servers, always profile your update operations. Use Laravel's built-in query logging, enable slow query logs on your database server, and consider implementing read/write database splitting for high-traffic applications.
The key is understanding that Eloquent gives you multiple tools for different situations. Don't default to the most convenient option – choose the right tool for your specific use case, server capacity, and performance requirements. When in doubt, measure first, then optimize.
Remember to always use database transactions for critical updates, implement proper error handling, and consider the impact on your server resources when designing bulk update operations. Your future self (and your server's CPU) will thank you.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.