<?php
use Illuminate\Support\Facades\DB;
public function createOrder(array $items, User $user)
{
return DB::transaction(function () use ($items, $user) {
// Create order
$order = Order::create([
'user_id' => $user->id,
'total' => 0,
]);
$total = 0;
foreach ($items as $item) {
// Create order items
$orderItem = $order->items()->create([
'product_id' => $item['product_id'],
'quantity' => $item['quantity'],
'price' => $item['price'],
]);
// Update inventory
$product = Product::findOrFail($item['product_id']);
if ($product->stock < $item['quantity']) {
throw new \Exception("Insufficient stock for {$product->name}");
}
$product->decrement('stock', $item['quantity']);
$total += $item['price'] * $item['quantity'];
}
// Update order total
$order->update(['total' => $total]);
return $order;
}, 3); // Retry 3 times on deadlock
}
<?php
use Illuminate\Support\Facades\DB;
public function transfer(Account $from, Account $to, float $amount)
{
DB::beginTransaction();
try {
// Deduct from source account
if ($from->balance < $amount) {
throw new \Exception('Insufficient funds');
}
$from->decrement('balance', $amount);
// Add to destination account
$to->increment('balance', $amount);
// Create transaction record
Transaction::create([
'from_account_id' => $from->id,
'to_account_id' => $to->id,
'amount' => $amount,
'type' => 'transfer',
]);
DB::commit();
return true;
} catch (\Exception $e) {
DB::rollBack();
\Log::error('Transfer failed: ' . $e->getMessage());
throw $e;
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
// Fire events only after transaction commits
protected $afterCommit = true;
protected static function booted()
{
static::created(function ($order) {
// This only runs if transaction commits
event(new OrderCreated($order));
});
}
}
Database transactions ensure multiple database operations succeed or fail together, maintaining data consistency. I wrap related operations in DB::transaction() which automatically commits on success and rolls back on exceptions. For manual control, I use DB::beginTransaction(), DB::commit(), and DB::rollBack(). Nested transactions use savepoints internally. The transaction closure receives attempts count for retry logic. Eloquent events fire after transactions commit when using $afterCommit property. For distributed transactions across services, I implement saga patterns. Transactions prevent partial updates that leave data in inconsistent states—critical for payment processing, inventory management, or multi-table updates. Proper transaction usage is fundamental to data integrity.