Navigation

Laravel

How to use `increment()` and `decrement()` on a column

Update Laravel model columns atomically in 2025 using increment() and decrement() methods. Perfect for counters, statistics, and preventing race conditions.

Table Of Contents

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!

More from Laravel