I have one model with associative data, with relation hasMany through,
Example-models:
One
OneTwo
Two
When I update my object One with containing objects OneTwo, I need to delete old objects OneTwo.
I use saveAssociated for save object One, with atomic = false.
I'm handling the transaction manually, example at fly:
$save = false;
$dataSource->begin();
try{
$One->OneTwo->deleteAll(conditions);
$saveAssociated( $one ); //if exception for anythings go catch
$save = true;
}catch( Exception $e ){
//print exception in log
}
if($save){
//commit
}else{
//rollback
}
but the block try catch is ignored for cake, in exception not go at the catch, continue normal.
as I can use a block try catch in cake??
Related
here's my code:
$id = 'wrong id';
try{
$myLaravelModel = MyLaravelModel::find($id);
$myLaravelModel->done = true; //here's error
}catch (Exception $e){
return 'error';
}
and when the code gets to the line with comment, I got error: "Creating default object from empty value", which is accurate, but I thought the catch block would catch that and return 'error'. Why is it not working?
You need to use findOrFail(). MyLaravelModel::findOrFail($id).
As it was said in here.
Not Found Exceptions
Sometimes you may wish to throw an exception if a model is not found.
This is particularly useful in routes or controllers. The findOrFail
and firstOrFail methods will retrieve the first result of the query;
however, if no result is found, a
Illuminate\Database\Eloquent\ModelNotFoundException will be thrown:
I separate the model queries on a trait class, because I hate to read a long block of codes with the model queries, and because I find it convenient if I reused the same function. But I found a problem once an error occur.
I was trying to run rollback function from eloquent once an error occur but, unfortunately, rollback wont work as I am expecting.
Am I doing it wrong?
Are there any other ways to implement this?
try {
DB::beginTransaction();
// UserDetails
$userdetailsID = $this->saveUserDetails($request,$userData->id);
if($userdetailsID){
$result = $this->saveUser(
$request,
$this->getHashValue($request->password),
$userdetailsID,
$userData->id,
$this->cleanTobeSafeAsDirectory(crypt(($userdetailsID.$userData->companyid), 'rl'))
);
if($result){
$updalodResult = $this->uploadThisImage( $request , 'images/uploads/users/'.$userdetailsID.'/icon/', '_'.$this->getHashValue($userdetailsID),'userImageAvatar');
if($updalodResult['success']){
$resul = $this->getThisUserDetials($userdetailsID);
$resul->photo_name = $updalodResult['filename'];
$resul->save();
$imageFilePath = 'images/uploads/users/'.$userdetailsID.'/icon/'.$updalodResult['filename'];
$this->cropImageJpegOnly($imageFilePath,$request->img_x,$request->img_y,$request->img_w,$request->img_h,$request->img_width,$request->img_height);
}
DB::commit();
return $this->returnAsAppSuccess('User information added.');
}
DB::rollBack();
return $this->returnAsAppError('Failed to user security details.' );
}
DB::rollBack();
return $this->returnAsAppError('Failed to user security details.' );
} catch (PDOException $e) {
DB::rollBack();
return $this->returnAsAppError('Failed to insert User information.');
}
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();
I have a large data set that needs to be written to the database when posted to the server, but it's possible that a bug in the client editor is added extra records that trigger an "integrity constraint violation".
My problem is that when I reach the point in the data where the error is trigger, then a lot of the previous data has already been updated in the database. I need to rollback and reject the posted data.
Here's my controller's action.
/**
* Handles saving data from the network editor.
*/
public function json_save()
{
if($this->request->is('post'))
{
$result = array();
$data = $this->request->input('json_decode');
if(isset($data->network_id) && !empty($data->network_id))
{
$dataSource = $this->Node->getDataSource();
$dataSource->begin();
$result['nodes'] = $this->updateModel($data->network_id, $this->Node, 'nodes', $data, array(
'ParamValue'
));
$result['connections'] = $this->updateModel($data->network_id, $this->Connection, 'connections', $data);
$dataSource->commit();
$this->viewClass = 'Json';
$this->set('result', $result);
$this->set('_serialize', array(
'result'
));
return;
}
}
throw new ErrorException('Posted data missing.');
}
My controller's updateModel function performs a few deletes and updates to the models $this->Node and $this->Connection.
How do I roll back upon an "integrity constraint violation" which is usually thrown during updating of the $this->Connection model.
I'm not sure if it's a PHP exception that I can catch and then do a rollback, or if there is a different way to catch it.
You can do a :
$dataSource->begin();
try {
// The queries here.
} catch (Exception $e) {
$dataSource->rollback();
throw $e;
}
$dataSource->commit();
I can't get my head around WHEN to throw and catch exceptions for when I call functions from classes.
Please imagine that my QuizMaker class looks like this:
// Define exceptions for use in class
private class CreateQuizException extends Exception {}
private class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (CreateQuizException $create_quiz_exception)
{
// There was an error creating the quiz in the database
return false;
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (InsertQuizException $insert_exception)
{
// Error inserting question
return false;
}
}
}
... and using this code, I use the class to create a new quiz in the database
class QuizMakerException extends Exception {}
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (QuizMakerException $quiz_maker_error)
{
// I want to handle any errors from these functions here
}
For this piece of code, I want to call a QuizMakerException if any of the functions don't perform what I want them to (at the moment they return TRUE or FALSE).
What is the correct way to go about catching when any of the functions in this code does not perform what I want them to? At the moment they simply return TRUE or FALSE.
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
Do I throw a QuizMakerException from within the catch of my functions?
What is the right thing to do?
Help!
Well typically in the function which throws the exception, say your InsertQuestions method, you don't want to catch any exceptions, you want to throw them or let ones occurring to "bubble up". Then your "controller" code can make the determination of how to handle the exception.
If your goal here is to halt if CreateQuizRecord fails I would wrap CreateQuizRecord and InsertQuestions each in their own try block.
One advantage of exceptions is they can tell you more than a simple bool pass/fail. Either extending your base exception into things like "Invalid_Parameter" and testing for specific exceptions or -less ideally- inferring from properties of the exception. You can nest your catch blocks to handle exceptions individually.
Do I throw a QuizMakerException from within the catch of my functions?
Yes. Typically your code under // Code to insert quiz would itself return an exception. Say if the Model failed to insert it might be raising a database exception. In which case you can let that database exception bubble up, or do what you sort of doing now and catch it simply to in turn throw another exception (kinda dumbs down your exceptions in a way, doing that though).
Do I really have to put lots of if/else statements between calling each function, I thought that was the whole point in exceptions, they simply halt the execution of further statements within the try/catch?
I look at it like this, each call which throws an exception and is followed by a subsequent call which depends on this one not throwing any exceptions, should be wrapped in a try block. Assuming you want to handle it gracefully, if you simply want it to error out and halt just don't handle the exception. You'll get an error and stack trace. Sometimes that is desirable.
You might want to change the structure a bit. Your class QuizMaker can become:
<?php
// Define exceptions for use in class
public class CreateQuizException extends Exception {}
public class InsertQuizException extends Exception {}
class QuizMaker()
{
// Define the items in my quiz object
$quiz_id = null;
$quiz_name = null;
// Function to create a quiz record in the database
function CreateQuizInDatabase()
{
try
{
$create_quiz = // Code to insert quiz
if (!$create_quiz)
{
// There was an error creating record in the database
throw new CreateQuizException("Failed inserting record");
}
else
{
// Return true, the quiz was created successfully
return true;
}
}
catch (Exception $create_quiz_exception)
{
// There was an error creating the quiz in the database
throw new CreateQuizException(
"Failed inserting record " .
$create_quiz_exception->getMessage()
);
}
}
function InsertQuestions()
{
try
{
$insert_quiz = // Code to insert quiz
if (!$insert_quiz)
{
// There was an error creating record in the database
throw new InsertQuizException("Failed inserting quiz in to database");
}
else
{
// Success inserting quiz, return true
return true;
}
}
catch (Exception $insert_exception)
{
// Error inserting question
throw new InsertQuizException(
"Failed inserting quiz in to database " .
$create_quiz_exception->getMessage()
);
}
}
}
Effectively, if you cannot insert the record correctly, you throw an Insert exception. If anything goes wrong with that block of code, you catch it and throw again an Insert exception. Same goes with the Create function (or any other ones you might have).
In the main block of code you can have:
try
{
// Create a blank new quiz maker object
$quiz_object = new QuizMaker();
// Set the quiz non-question variables
$quiz_object->quiz_name = $_POST['quiz_name'];
$quiz_object->quiz_intro = $_POST['quiz_intro'];
//... and so on ...
// Create the quiz record in the database if it is not already set
$quiz_object->CreateQuizRecord();
// Insert the quiz in to the database
$quiz_object->InsertQuestions();
}
catch (InsertQuizException $insert_exception)
{
// Insert error
}
catch (CreateQuizException $create_quiz_exception)
{
// Create error
}
catch (Exception $quiz_maker_error)
{
// Any other error
}
If you don't want to have the multiple catch block there, just keep the catch(Exception) one and then check in it the type of each exception thrown to specify the actions taken from there.
HTH
The best thing to do is to not have to thhrow exceptions in the first place.
Exceptions are thrown when the program crashes and they are not made to handle wrong output.
If your function returns anything (even its the wrong thing) it doesn't need an exception.
To answer your question, if it's necessaryto use a lot of ifs/elses then you must use them.