I've decided to give an answer to this question because I think it can be solved using a simpler syntax than the convoluted try-catch block. The Laravel documentation is pretty brief on this subject.
Instead of using try-catch, you can just use the DB::transaction(){...}
wrapper like this:
// MyController.php
public function store(Request $request) {
return DB::transaction(function() use ($request) {
$user = User::create([
'username' => $request->post('username')
]);
// Add some sort of "log" record for the sake of transaction:
$log = Log::create([
'message' => 'User Foobar created'
]);
// Lets add some custom validation that will prohibit the transaction:
if($user->id > 1) {
throw AnyException('Please rollback this transaction');
}
return response()->json(['message' => 'User saved!']);
});
};
You should then see that the User and the Log record cannot exist without eachother.
Some notes on the implementation above:
return
the transaction, so that you can use the response()
you return within its callback.throw
an exception if you want the transaction to be rollbacked (or have a nested function that throws the exception for you automatically, like an SQL exception from within Eloquent).id
, updated_at
, created_at
and any other fields are AVAILABLE AFTER CREATION for the $user
object (for the duration of this transaction). The transaction will run through any of the creation logic you have. HOWEVER, the whole record is discarded when the AnyException
is thrown. This means that for instance an auto-increment column for id
does get incremented on failed transactions.Tested on Laravel 5.8