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))
🧪 Force Rollback Manually
If you want to rollback without throwing (e.g. for simulation/test):
-
Returns
false
-
No exception handlers are triggered
-
Transaction is rolled back cleanly
🔁 Shortcuts
RunDbTransaction::runTransaction(fn() => ...)
Basic one-liner with safe rollback handling:
RunDbTransaction::runOrFail(fn() => ...)
Same as above, but rethrows the exception if one happens. When you only need managed transaction block:
Also works with DO_ROLLBACK
– in that case it rolls back without throwing.
💡 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 |