Joomla 2.5 - Rollback on model method save() - php

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?

Related

Why error handling is not working in Laravel

I have set the debug to true on .env file. Added the exceptions correctly but when i am passing invalid or not exist in my database its showing me 404 error but here I put the custom error handling value. Here is my code. (also I put "Use Expectation;" on top so no need of \Expectation)
public function show($id)
{
//only one author with id
try
{
$event = Event::with('eventCategory')->findOrFail($id);
return new EventsResource($event);
//return one author
}
catch(Expectation $e)
{
report($e);
return response()->json(['status'=> false, 'message'=>'invalid data'],200);
}
}
Do you mean Exception? Because in your question it's Expectation...
As #user3532758 is implying, you probably want to be catching the base Exception not Expectation.
Also make sure you are referencing Exception from the root namespace, assuming the code you have shown is in a Controller:
try {
...
} catch (\Exception $e) {
...
}
PHP Manual - Classes - Exception

how to use return type a class in php 7

I tried my code as below. but in this, I return a blank object when an error occurred.
Is it ok to write return blank object? when the return type is class.
public static function createBook($book_data): BookModel
{
try {
$book = new BookModel;
$book->fill($book_data);
$book->save();
return $book;
}
catch(\Exception $e) {
log::error($e->getMessage());
$book = new BookModel;
return $book;
}
}
You're in for pain if you return empty objects:
If you use database's auto increment ids, then it wont exist on this empty model.
Any other required fields will not be filled in. (E.g., book with no author and no content?)
Although in some situations empty state objects are okay, if you have to ask then you most likely will handle it incorrectly anyway.
Since you have to check against this empty object anyway, returning null is still an okay solution. If you're on php7.1 then the return type can stay (note the questionmark).
public static function createBook($book_data): ?BookModel
{
try {
$book = new BookModel;
$book->fill($book_data);
$book->save();
return $book;
}
catch(\Exception $e) {
log::error($e->getMessage());
return null;
}
}
IMO, a better aproach would be to allow the error to propagate up and let you catch it somewhere else. Looks like you're using laravel, so the error will be logged anyway. So, for example, if you're in a database transaction, the transaction can fail safely:
public static function createBook($book_data): BookModel
{
$book = new BookModel;
$book->fill($book_data);
$book->save();
return $book;
}
I say it depends on what you want to do if (in this case) the exception is thrown.
If you want to let the app know that something went wrong, you should return false. Returning an empty BookModel is basically saying that everything went fine and it's much easier to check if something is false than checking if an attribute of an object is empty.
If it doesn't really mind that the book is empty (i.e. your code doesn't assume books are always filled), I think it is okay to return an empty book. However i'd change the code a bit:
public static function createBook($book_data): BookModel
{
$book = new BookModel();
try {
$book->fill($book_data);
$book->save();
}
catch(\Exception $e) {
log::error($e->getMessage());
}
return $book;
}

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

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()));
}

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();

Handling exceptions for multiple calls to functions of a class

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.

Categories