Database Transaction Helper
RunDbTransaction
RunDbTransaction
is a fluent helper for running safe and clean database transactions in Laravel.
It handles edge cases around rollback levels, especially when using custom exceptions like DefaultException
(common in our codebase).
⚠️ Why This Matters
When a DefaultException
is thrown (one that already performs a rollback), using DB::beginTransaction()
with a try-catch around it can lead to double rollback errors or broken transaction levels.
That's the main problem RunDbTransaction
solves:
-
It detects if rollback has already happened (e.g. inside
DefaultException
) -
Prevents Laravel’s transaction level from going out of sync
-
Lets you catch and handle exceptions cleanly — even continue without rollback if needed
It also works great with regular exceptions that don’t trigger rollback on their own.
✅ Basic Usage
$successful = RunDbTransaction::create()
->transaction(fn() => $this->mainTransaction())
->run();
-
Always call
run()
at the end. -
Returns
true
if transaction was committed. -
Returns
false
if it was rolled back.
⚙️ Methods
→ transaction(callable $callback)
Defines the main code block that runs inside the transaction.
->transaction(fn() => $this->doSomething())
→ intercept(callable $handler)
Optional. Runs only if an exception was thrown and rollback hasn’t yet happened.
This is where you can handle something like DefaultException
, and still choose to commit the transaction.
->intercept(fn($e) => $this->handleBeforeRollback($e))
-
Return truthy value → Commit happens
-
Return falsy value → Rollback proceeds
→ catch(callable $handler)
Optional. Runs after rollback. Even if intercept
ran but returned false.
->catch(fn($e) => $this->handleAfterRollback($e))
🚀 Full Example with All Handlers
$successful = RunDbTransaction::create()
->transaction(fn() => $this->mainTransaction())
->intercept(fn($e) => $this->interceptException($e))
->catch(fn($e) => $this->catchException($e))
->run();
🧪 Force Rollback Manually
You can force a rollback by returning a special flag:
$committed = RunDbTransaction::create()
->transaction(function () {
$this->doSomething();
return RunDbTransaction::DO_ROLLBACK;
})
->run();
-
Returns
false
-
No exception handlers are triggered
-
Transaction is rolled back cleanly
🔁 Shortcuts
RunDbTransaction::runTransaction(fn() => ...)
Basic one-liner with safe rollback handling:
$success = RunDbTransaction::runTransaction(fn() => $this->mainTransaction());
$success = RunDbTransaction::create()
->transaction(fn() => $this->mainTransaction())
->run();
RunDbTransaction::runOrFail(fn() => ...)
Same as above, but rethrows the exception if one happens. When you only need managed transaction block:
RunDbTransaction::runOrFail(fn() => $this->mainTransaction());
Also works with DO_ROLLBACK
– in that case it rolls back without throwing.
Also shorthand for:
$successful = RunDbTransaction::create()
->transaction(fn() => $this->mainTransaction())
//This shape is required when intercept handler is needed!
->intercept(fn($e) => $this->handleBeforeRollback($e))
->catch(function ($e) {
throw $e; //Just rethrows the exception
})
->run();
Use this shorthand when you want error to propagate further and only need save transaction block.
💡 Summary Table
Method | Description |
---|---|
transaction() |
Required. Code block to wrap in a transaction |
intercept() |
Optional. Runs if exception is thrown before rollback |
catch() |
Optional. Runs after rollback, always gets the exception |
run() |
Executes the transaction logic |
runTransaction() |
Shorthand with rollback detection |
runOrFail() |
Shorthand that rethrows exception |
DO_ROLLBACK |
Special return value to force rollback without exception |