Table Of Contents
- The Race Condition Problem
- Laravel Increment and Decrement Methods
- Advanced Increment/Decrement Patterns
The Race Condition Problem
Multiple users liking a post simultaneously can cause incorrect counts. Traditional update methods have race conditions, but Laravel's atomic operations solve this:
// DANGEROUS - Race condition possible
$post = Post::find(1);
$post->likes = $post->likes + 1; // User A reads likes = 10
$post->save(); // User B also reads likes = 10
// Both save likes = 11 (should be 12!)
// SAFE - Atomic database operation
$post = Post::find(1);
$post->increment('likes'); // Database handles concurrency
// OR
Post::where('id', 1)->increment('likes');
Laravel Increment and Decrement Methods
Different ways to update numeric columns safely:
// Basic increment/decrement
$user = User::find(1);
$user->increment('login_count'); // +1
$user->decrement('credits'); // -1
// Custom amount
$user->increment('points', 50); // +50
$user->decrement('balance', 25.99); // -25.99
// Multiple columns at once
$user->increment('posts_count');
$user->increment('reputation', 10);
// Query builder approach (no model instance needed)
User::where('id', 1)->increment('login_count');
Post::where('published', true)->increment('views');
// Bulk operations
User::where('active', true)->increment('notification_count');
Product::where('category_id', 5)->decrement('stock', 10);
// With additional column updates
$user->increment('login_count', 1, [
'last_login_at' => now(),
'ip_address' => request()->ip()
]);
$post->increment('views', 1, [
'last_viewed_at' => now(),
'viewer_ip' => request()->ip()
]);
Advanced Increment/Decrement Patterns
Real-world scenarios where atomic operations prevent data corruption:
// E-commerce inventory management
public function purchaseProduct($productId, $quantity)
{
DB::transaction(function () use ($productId, $quantity) {
$product = Product::lockForUpdate()->find($productId);
if ($product->stock < $quantity) {
throw new InsufficientStockException();
}
// Atomic decrement prevents overselling
$product->decrement('stock', $quantity);
$product->increment('sold_count', $quantity);
});
}
// Social media engagement
public function likePost($postId)
{
$post = Post::find($postId);
// Check if user already liked
if (!$post->likes()->where('user_id', auth()->id())->exists()) {
// Create like record
$post->likes()->create(['user_id' => auth()->id()]);
// Increment counter atomically
$post->increment('likes_count');
// Update user's activity
auth()->user()->increment('likes_given');
}
}
// Content statistics and analytics
public function recordPageView($pageId, $userId = null)
{
// Increment total views
Page::where('id', $pageId)->increment('views');
// Track unique views
if ($userId && !PageView::where('page_id', $pageId)->where('user_id', $userId)->exists()) {
PageView::create(['page_id' => $pageId, 'user_id' => $userId]);
Page::where('id', $pageId)->increment('unique_views');
}
// Daily statistics
$today = now()->format('Y-m-d');
DailyStats::updateOrCreate(
['date' => $today, 'page_id' => $pageId],
['views' => 0]
)->increment('views');
}
// Financial operations with precision
public function processPayment($userId, $amount)
{
DB::transaction(function () use ($userId, $amount) {
$user = User::lockForUpdate()->find($userId);
// Use increment/decrement for monetary values
$user->decrement('wallet_balance', $amount);
$user->increment('total_spent', $amount);
// Log transaction
Transaction::create([
'user_id' => $userId,
'amount' => $amount,
'type' => 'payment',
'balance_after' => $user->fresh()->wallet_balance
]);
});
}
// Conditional increments
public function updateUserStreak($userId)
{
$user = User::find($userId);
$lastActivity = $user->last_activity_at;
if ($lastActivity && $lastActivity->isYesterday()) {
// Continue streak
$user->increment('current_streak');
// Update longest streak if needed
if ($user->current_streak > $user->longest_streak) {
$user->update(['longest_streak' => $user->current_streak]);
}
} elseif (!$lastActivity || !$lastActivity->isToday()) {
// Reset streak
$user->update(['current_streak' => 1]);
}
$user->update(['last_activity_at' => now()]);
}
Use increment()
and decrement()
for any numeric column that multiple users might update simultaneously. These methods generate atomic SQL like UPDATE table SET column = column + 1
which prevents race conditions and ensures data consistency.
Related: Laravel Collections: Beyond Basic Array Operations | Database Design: Fundamentals of Good Database Architecture | Database Design Patterns: Complete Guide 2025
Share this article
Add Comment
No comments yet. Be the first to comment!