update(), updateOrInsert(), increment(), decrement()
Concept
Update methods modify existing rows. Like inserts, Query Builder updates bypass model events and casts — plan accordingly.
update(array $data): UPDATE table SET ... WHERE .... Returns the number of affected rows. Requires a WHERE constraint in production — updating without WHERE affects all rows.
updateOrInsert(array $attributes, array $values = []): If a row matching $attributes exists, updates it with $values. If not, inserts merged data. Similar to Eloquent's updateOrCreate but returns a boolean, not a model.
increment(string $column, int|float $amount = 1, array $extra = []): Atomic UPDATE SET col = col + N. Safe for concurrent updates — avoids race conditions that would occur with read-modify-write in PHP. $extra allows updating other columns in the same query.
decrement(string $column, int|float $amount = 1, array $extra = []): Inverse.
update(DB::raw(...)): For complex update expressions like SET col = JSON_SET(col, '$.key', 'value').
Mass updates: User::where('active', false)->update(['deleted_at' => now()]) — never loads models. Fast but no model events.
updateOrInsert vs upsert: updateOrInsert works one row at a time (SELECT + INSERT or UPDATE). upsert is a single SQL statement supporting batch rows — prefer upsert for batch operations.
Code Example
<?php
use Illuminate\Support\Facades\DB;
// Basic update
$affected = DB::table('users')
->where('id', 1)
->update(['name' => 'Alice Smith', 'updated_at' => now()]);
// Returns: int (number of rows changed)
// Mass update — no model events
DB::table('users')
->where('last_login_at', '<', now()->subMonths(6))
->update(['active' => false, 'updated_at' => now()]);
// updateOrInsert
DB::table('user_settings')->updateOrInsert(
['user_id' => 1, 'key' => 'notifications'], // search criteria
['value' => 'email', 'updated_at' => now()] // values to set (or update)
);
// increment / decrement — atomic, race-condition safe
DB::table('products')->where('id', 5)->increment('stock', 10);
DB::table('products')->where('id', 5)->decrement('stock'); // -1
// increment with extra columns in same query
DB::table('posts')
->where('id', $postId)
->increment('views', 1, ['last_viewed_at' => now()]);
// SQL: UPDATE posts SET views = views + 1, last_viewed_at = ? WHERE id = ?
// Eloquent increment — works the same, model events DON'T fire
User::where('active', true)->increment('login_count');
// Using DB::raw in update
DB::table('products')
->where('id', 1)
->update([
'metadata' => DB::raw('JSON_SET(metadata, "$.last_checked", NOW())'),
'updated_at' => now(),
]);
// Multiple increments in one query
DB::table('stats')
->where('date', today())
->update([
'page_views' => DB::raw('page_views + 1'),
'unique_visits' => DB::raw("unique_visits + IF(is_new_visitor, 1, 0)"),
]);
// Safe pattern — always include WHERE
$rowsUpdated = DB::table('orders')
->where('status', 'pending')
->where('created_at', '<', now()->subDays(7))
->update(['status' => 'expired']);
if ($rowsUpdated === 0) {
// No rows matched — no update happened
}