Transaction in Laravel rolled back incompletely - laravel

I have an array of Organization model. Each Organization contains an array of User model.
There is a many-to-many relationship between them using the OrganizationUser model.
If I have problems inserting an Organization, an User, or a relationship between them, I want to roll everything back.
But I also use "try catch" blocks to see at which stage the error occurred.
To test this, I explicitly made a mistake in the OrganizationUser relationship object. I'm using an User id that can't be in the Organization.
This rolls back the Organization, relationships, and first User, but all other Users are added to the database.
DB::beginTransaction();
$org->save();
foreach ($org->user_list as $user) {
try {
$user->save();
} catch (\Exception $exception) {
DB::rollback();
Log::error('Error user insert : '. $user->inn .' '. $exception->getMessage());
}
try {
$orgs_user = OrganizationUser::create([
'user_id' => 80,
'org_id' => $org->id,
]);
} catch (\Exception $exception) {
DB::rollback();
Log::error('Insert user_org relationship : ' . $exception->getMessage());
}
}
When I didn't use the "try catch" block it worked well

When you call DB::rollBack() (by the way it's rollBack with a capital B), that ends the transaction. The rest of the queries will execute as normal.
The safest way to use transactions is to put the entire thing in a single try catch. The \Exception itself should contain enough information to know what caused it in its message or stack trace.
try {
DB::beginTransaction();
$org->save();
foreach ($org->user_list as $user) {
$user->save();
$orgs_user = OrganizationUser::create([
'user_id' => 80,
'org_id' => $org->id,
]);
}
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
}
It is a bit verbose, but you can also use the DB::transaction() method.
DB::transaction(function () use ($org) {
$org->save();
foreach ($org->user_list as $user) {
$user->save();
$orgs_user = OrganizationUser::create([
'user_id' => 80,
'org_id' => $org->id,
]);
}
});

DB::rollback does not stop the execution of the code.
If you are in a catch block the transaction will be rolled back but the next block (as well as the rest of the iteration) will continue.
Use a break; after the rollback to break out of the loop completely like below:
DB::beginTransaction();
$org->save();
foreach ($org->user_list as $user) {
try {
$user->save();
} catch (\Exception $exception) {
DB::rollback();
Log::error('Error user insert : '. $user->inn .' '. $exception->getMessage());
break;
}
try {
$orgs_user = OrganizationUser::create([
'user_id' => 80,
'org_id' => $org->id,
]);
} catch (\Exception $exception) {
DB::rollback();
Log::error('Insert user_org relationship : ' . $exception->getMessage());
break;
}
}
You can also return; or just exit() depending on how you want to handle the error

Related

give only authenticated user ability to fetch his own data with Laravel API and Sanctum

i have this function for get orders for only authenticated user:
function show($uid) {
try {
$user = User::findOrFail($uid);
$orders = $user->orders;
return $orders;
}catch (\Exception $e) {
return response()->json(['messsage' => "cannot show order for this user"]);
}
}
it is a end point for API in this route:
Route::group(['middleware' => ['auth:sanctum']], function () {
Route::get('/order/{id}', [OrdersController::class, 'show']);
});
but now if anyone just add any uid, he can display all orders...
my question is how can i protect this function so just auth user can fetch data: and i have used Sanctum in my project
in laravel with blade i just do like this:
function show() {
$uid = auth()->id();
try {
$user = User::findOrFail($uid);
$orders = $user->orders;
return $orders;
}catch (\Exception $e) {
return response()->json(['messsage' => "cannot show order for this user"]);
}
}
Thank you all...... I have found the solution, i could find the id of Authenticated user simply by this since i use the guard (sanctum):
auth('sanctum')->user()->id
this will give me the id for auth user depending on the token.
and the solution will be like this:
function show(Request $request) {
try {
$uid = auth('sanctum')->user()->id;
$user = User::findOrFail($uid);
$orders = $user->orders;
return $orders;
}catch (\Exception $e) {
return response()->json(['messsage' => "cannot show order for this user"]);
}
}

Laravel : Unable to insert in database after a transaction has rolled back

I successfully use transaction in Laravel to this point : I cannot execute another query after transaction has rolled back.
DB::beginTransaction();
try {
// DO some stuff
// Commit transaction
DB::commit();
} catch (\Exception $e) {
DB::rollback();
// This statement has no effect
DB:insert('something');
}
If I execute DB:insert('something'); just before the transaction, it works fine.
Anything I do wrong ?
No error is thrown. The DB::insert just does not insert anything in the database.
The problem came from elsewhere, sorry for the inconvenience.
For the record, be careful if you use a model instance in your transaction and want to use it after the rollback.
$myModel = MyModel::find(1);
$myModel->someData = 'model to delete';
$myModel->save();
DB::beginTransaction();
try {
$myModel->delete();
DB::commit();
} catch (\Exception $e) {
DB::rollback();
// This won't work : tries to make an insert
$myModel->someData = 'some data';
$myModel->save();
// This will work : will update the instance
$myModel = MyModel::find(1);
$myModel->someData = 'some data';
$myModel->save();
}

Want to redirect to a page

I want to redirect to the product page. This is my controller function.
public function RegisterBusiness(Request $request){
DB::beginTransaction();
try {
if(session()->has('user_id')){
$request['mybiz_users_id']=Session()->get('user_id');
}
$this->businessTempService->insertRegisteredBusinessDetails($request);
DB::commit();
return $this->sendResponse('success', 'Registered Successfully, Thank You!', '', 200,route('web.registration.advanced-product-registration'));
} catch (Exception $ex) {
DB::rollback();
Log::error($ex);
return $this->sendResponse('error', 'Something went wrong', '', 422);
}
}
But it is not working.
It loads this page
This is not an error, its a json response , if you redirect to product page then change
return $this->sendResponse('error', 'Something went wrong', '', 422);
to
return view('product.index');
or, with name route :
return route('product');
You should use \request()->expectsJson() to find out whether you need a json response or not.
if (\request()->expectsJson()){
return $this->sendResponse('success', 'Registered Successfully, Thank You!', '', 200,route('web.registration.advanced-product-registration'));
}else{
return back();
}

Laravel transaction saves data into DB even when it's rollbacked

I send a request, trying to save multiple documents at once.
Here is my code
DB::beginTransaction();
try {
foreach ($request->documents as $index => $documentInfo) {
// Check
if (// some statement) {
$documentExists = Document::where([
// some checks
])
->exists();
if ($documentExists) {
throw new \Exception("Error Processing Request", 1);
}
}
// Assign document properties
$document->save();
}
DB::commit();
} catch (\Exception $e) {
DB::rollBack();
if ($request->ajax()) {
return response()->json([
'success' => false,
'document' => $index,
'message' => '// message',
]);
}
return redirect()->back()->with('error', '// message');
}
The thing that happens is that I check every document for its own unique values. If two documents with same values try to get saved I want to return an error.
I tried uploading two documents with the same values, the error is returned on the second document and the transaction must fail, but the first document gets saved into the database.
I don't want this, if there is an error I don't want any documents saved into the DB.

Return Date To Client From Within DB::Transaction Closure()

I am executing several database saves within my db transaction closure:
DB::transaction(function() {
...
});
However what i want to do now is when a transaction fails instead of throwing an exception i want to return a custom JSON object straight t the client, if the transactions succeeds i want to do the same thing.
This is my object:
return [
'code' => '',
'message' => '',
'data' => []
];
How would i return the above to the client from within the closure?
Instead of DB::transaction Closure you can use DB::beginTransaction, DB::commit and DB::rollback methods in order to have more control in code, you can wrap your DB actions like this :
DB::beginTransaction();
try {
DB::insert(...);
DB::insert(...);
DB::insert(...);
//If everything is ok we commit
DB::commit();
return response()->json(["status" => "success"])
} catch (Exception $e) {
//something goes wrong, we rollback
DB::rollback();
return response()->json(["error" => "Some error"]);
}
You can research more in database transaction docs

Resources