Laravel - Check query execution after attach() and detach() - php

After I execute this
$shop->articles()->attach($article_id);
or this
$shop->articles()->detach($article_id);
how do I make sure they actually get executed? In the first case it returns void, but also it saves automatically so I cannot use the save function to check it. In the second case it returns int, but what does it represent?

You do it with try/catch and DB::transaction/commit/rollback.
try{
\DB::transaction(function() use($shop, $article_id) {
$shop->articles()->attach($article_id);
});
\DB::commit();
} catch (\Exception $e) {
\DB::rollback();
return redirect()->back()->withErrors(collect($e->getMessage()));
}

Related

Joomla 2.5 - Rollback on model method save()

This is what I'm trying to do:
I save my item in the model, then I call a method that can throw an exception if something goes wrong.
If I catch the exception, I want to rollback, this is my override of the save method:
public function save($data) {
$db = JFactory::getDbo();
$db->transactionStart();
$retVal = parent::save($data);
if ($retVal) {
try {
$this->_doSomething();
$db->transactionCommit();
} catch (Exception $exc) {
$db->transactionRollback();
JFactory::getApplication('administrator')
->enqueueMessage($exc->getMessage(), 'error');
return false;// THIS MAKES ROLLBACK FAIL, TO MAKE IT WORK I HAVE TO COMMENT THIS.
}
}
return $retVal;
}
It only works if my method returns true anyway.
To me it should be more correct that the method in this case returns false, because the saving is not done and because if it returns true, Joomla outputs a misleading message as if saving succeded.
But if it does rollback somehow it fails.
What makes rollback fail if the method save() returns false?

Try/catch not working with laravel 5.4

I know there are a lot of answers there about this question but none of them really helped me.
// update name
Route::put('/profile/update', function(Request $request){
$name = $request->input('name');
try{
echo DB::table('users')->where('id',Auth::id())->update(['name' => $name]);
}
catch(\Exception $e){
// do task when error
echo $e->get_message();
}
});
I have also tried delete method but that is also not working can you please figure out what is going on.
thanks.
2020 Update
Laravel mostly uses camelCase for nomenclature so use:
catch(\Exception $e){
// do task when error
echo $e->getMessage();
}
If not then just dump full $e to see it through:
catch(\Exception $e){
// do task when error
dd($e);
}
snake_case nomenclature is only used for table names in Laravel.
in my case, I must use \Throwable instead of \Exception
try {
//code
} catch (\Throwable $e) {
dd($e);
}
from your code it seems like the code will never hit the catch which is nothing to do with laravel actually. your issue is a SQL one.
you're trying to update a record and updating a none-existing row will never fail in SQL. so I suggest to handle the case manually by checking the result value and replacing the try and catch with if else
BTW #Learner is 100% right about get_message() it's not in laravel as I know replace it in the future with getMessage()
Change echo $e->get_message(); to echo $e->getMessage();, Exception class dose not have get_message() function.

Laravel 5 - try catch exception not working

I am new to laravel5 and this code fails to catch all the exceptions.
I don't know what's wrong, please help.
public function delete($id)
{
$sql = $this->deleteSql();
DB::beginTransaction();
try {
$deleteData = Db::delete($sql, ['id' => $id]);
if (!$deleteData) {
return false;
}
return true;
} catch (\Exception $e) {
DB::rollback();
return $e->getMessage();
}
DB::commit();
}
It will give me :
Illuminate\Database\QueryException: SQLSTATE[22P02]:
and
Caused by
PDOException: SQLSTATE[22P02]:
Your method is not going to catch block at all since it is returning bool from try block.
Also I don't know what made you use try...catch block.
I more error:
Db::delete($sql, ['id' => $id]);
If you have defined Db, then it is okay. But if not, then it will throw error.
Also there is no need to use transactions, unless you are testing your application with any PHP Testing tool like phpUnit / phpSpec, etc.
Solution:
There is no need to use try..catch block at all.
Just call the delete method on the model that you wish to delete, and you will be done, like so:
public function delete($id)
{
YourModel::delete($id); // Replace YourModel with the model you wish to delete
\Session::flash('delete_message', 'Model has been successfully deleted.');
return redirect()->back(); // or anywhere else of your choice
}
Hope this helps you out. Happy Coding. Cheers.

Laravel: Using try...catch with DB::transaction()

We all use DB::transaction() for multiple insert queries. In doing so, should a try...catch be placed inside it or wrapping it? Is it even necessary to include a try...catch when a transaction will automatically fail if something goes wrong?
Sample try...catch wrapping a transaction:
// try...catch
try {
// Transaction
$exception = DB::transaction(function() {
// Do your SQL here
});
if(is_null($exception)) {
return true;
} else {
throw new Exception;
}
}
catch(Exception $e) {
return false;
}
The opposite, a DB::transaction() wrapping a try...catch:
// Transaction
$exception = DB::transaction(function() {
// try...catch
try {
// Do your SQL here
}
catch(Exception $e) {
return $e;
}
});
return is_null($exception) ? true : false;
Or simply a transaction w/o a try...catch
// Transaction only
$exception = DB::transaction(function() {
// Do your SQL here
});
return is_null($exception) ? true : false;
In the case you need to manually 'exit' a transaction through code (be it through an exception or simply checking an error state) you shouldn't use DB::transaction() but instead wrap your code in DB::beginTransaction and DB::commit/DB::rollback():
DB::beginTransaction();
try {
DB::insert(...);
DB::insert(...);
DB::insert(...);
DB::commit();
// all good
} catch (\Exception $e) {
DB::rollback();
// something went wrong
}
See the transaction docs.
If you use PHP7, use Throwable in catch for catching user exceptions and fatal errors.
For example:
DB::beginTransaction();
try {
DB::insert(...);
DB::commit();
} catch (\Throwable $e) {
DB::rollback();
throw $e;
}
If your code must be compartable with PHP5, use Exception and Throwable:
DB::beginTransaction();
try {
DB::insert(...);
DB::commit();
} catch (\Exception $e) {
DB::rollback();
throw $e;
} catch (\Throwable $e) {
DB::rollback();
throw $e;
}
You could wrapping the transaction over try..catch or even reverse them,
here my example code I used to in laravel 5,, if you look deep inside DB:transaction() in Illuminate\Database\Connection that the same like you write manual transaction.
Laravel Transaction
public function transaction(Closure $callback)
{
$this->beginTransaction();
try {
$result = $callback($this);
$this->commit();
}
catch (Exception $e) {
$this->rollBack();
throw $e;
} catch (Throwable $e) {
$this->rollBack();
throw $e;
}
return $result;
}
so you could write your code like this, and handle your exception like throw message back into your form via flash or redirect to another page. REMEMBER return inside closure is returned in transaction() so if you return redirect()->back() it won't redirect immediately, because the it returned at variable which handle the transaction.
Wrap Transaction
try {
$result = DB::transaction(function () use ($request, $message) {
// execute query 1
// execute query 2
// ..
});
// redirect the page
return redirect(route('account.article'));
} catch (\Exception $e) {
return redirect()->back()->withErrors(['error' => $e->getMessage()]);
}
then the alternative is throw boolean variable and handle redirect outside transaction function or if your need to retrieve why transaction failed you can get it from $e->getMessage() inside catch(Exception $e){...}
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 see that in this setup the User and the Log record cannot exist without eachother.
Some notes on the implementation above:
Make sure to return anything the transaction, so that you can use the response() you return within its callback as the response of the controller.
Make sure to throw an exception if you want the transaction to be rollbacked (or have a nested function that throws the exception for you automatically, like any SQL exception from within Eloquent).
The id, updated_at, created_at and any other fields are AVAILABLE AFTER CREATION for the $user object (for the duration of this transaction at least). The transaction will run through any of the creation logic you have. HOWEVER, the whole record is discarded when SomeCustomException is thrown. An auto-increment column for id does get incremented though on failed transactions.
Tested on Laravel 5.8
I'm using Laravel 8 and you should wrap the transaction in a try-catch as follows:
try {
DB::transaction(function () {
// Perform your queries here using the models or DB facade
});
}
catch (\Throwable $e) {
// Do something with your exception
}
in laravel 8, you can use DB::transaction in try-catch.
for example :
try{
DB::transaction(function() {
// do anything
});
}
catch(){
// do anything
}
if each of query be failed on try, the catch block be run.
First: using PostgreSQL database in Laravel makes things more tricky.
If you don't rollback after a transaction error, each futher queries will throw this error In failed sql transaction: ERROR: current transaction is aborted, commands ignored until end of transaction block. So if you can't save original error message in a table BEFORE the rollback.
try {
DB::beginTransaction(); //start transaction
$user1 = User::find(1);
$user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception $exception) {
$user2 = User::find(2); // ko, "In failed sql transaction" error
$user2->update(['field' => 'value']);
}
try {
DB::beginTransaction(); //start transaction
$user1 = User::find(1);
$user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception $exception) {
DB::rollBack();
$user2 = User::find(2); // ok, go on
$user2->update(['field' => 'value']);
}
Second: pay attention to Eloquent model attributes system.
Eloquent model keeps changed attributes after an update error, so if we want to update that model inside the catch block, we need to discard bad attributes. This isn't a dbtransaction affair, so the rollback command is useless.
try {
DB::beginTransaction(); //start transaction
$user1 = User::find(1);
$user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception|Error $exception) {
DB::rollBack();
$user1->update(['success' => 'false']); // ko, bad update again
}
try {
DB::beginTransaction(); //start transaction
$user1 = User::find(1);
$user1->update(['money' => 'not_a_number']); //bad update
}
catch(Exception|Error $exception) {
DB::rollBack();
$user1->discardChanges(); // remove attribute changes from model
$user1->update(['success' => 'false']); // ok, go on
}

Laravel DB::transaction() return value

It's my first time to use DB::transaction() but how exactly does it work if a transaction fails or is successful? In the example below, do I have to manually assign a value to return true, or if it fails will the method either return false or totally exit the transaction (therefore skipping the rest of the code)? The docs aren't so helpful on this.
use Exception;
use DB;
try {
$success = DB::transaction(function() {
// Run some queries
});
print_r($success);
} catch(Exception $e) {
echo 'Uh oh.';
}
Solution
I wrote down this solution for others who might be wondering.
Since I was more concerned about returning a boolean value depending on the success of my query, with a few modifications it now returns true/false depending on its success:
use Exception;
use DB;
try {
$exception = DB::transaction(function() {
// Run queries here
});
return is_null($exception) ? true : $exception;
} catch(Exception $e) {
return false;
}
Take note that the variable $exception is never returned since if something goes wrong with your query, the catch is immediately triggered returning false. Thanks to #ilaijin for showing that an Exception object is thrown if something goes wrong.
By giving a look at function transaction it does its process inside a try/catch block
public function transaction(Closure $callback)
{
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block
// and if we catch any exception we can rollback the transaction
// so that none of the changes are persisted to the database.
try
{
$result = $callback($this);
$this->commit();
}
// If we catch an exception, we will roll back so nothing gets messed
// up in the database. Then we'll re-throw the exception so it can
// be handled how the developer sees fit for their applications.
catch (\Exception $e)
{
$this->rollBack();
throw $e;
}
So throws an Exception (after the rollback) if fails or returns $result, which is the result of your callback
There is a short version if you want to use the default transaction method that ships with Laravel without handling it manually.
$result = DB::transaction(function () {
// logic here
return $somethingYouWantToCheckLater;
});
You can also use the following
DB::rollback();

Categories