Nested Laravel Try catch not throwing inner Exception, only outer - php

I'm a little bit fuzzy with Try Catch blocks, up until now i've just put some code inside them and tried to catch the error. I'm now using them in Laravel but I cant seem to get the exception to fire correctly when nesting statements, and i'm just wondering if someone can explain why or point me in the right direction!
I have 3 models, cases, members and documents.
Heres what I want to achieve
Try to store cases, members and any documents upload (including moving documents to storage folder). If any fail, revert everything.
I am running the cases and members store inside a DB::transaction function which is working great, but I then want to run my document move/store in the document model externally.
When I save my form, the case store function is whats fired first.
Here are my models/controllers so far
Case Controller
public function store(AddCaseRequest $request)
{
//Try to create a new case
try{
//New Case
$case = new Cases;
//New Member
$member = new Members;
DB::transaction(function() use ($case, $member) {
//Save Case
$case->fill(request()->all())->save();
//Save Member
$member->fill(request()->all())->save();
//Documents
if(request()->has('document')){
//Loop the documents and store
foreach(request()->document as $doc){
$document = with(new Documents)->storeNewDocument($doc, $case->id, 'case', 'cases');
}
}
}, 3);
//Redirect back to view page
return redirect()->route('cases.view', [$case->id]);
}
//Catch the error
catch(\Exception $e){
//Log the error
Log::debug('Cases Create Error', (Array) $e->getMessage());
//Redirect back
return redirect()->back() //Redirect back
->withErrors(['Whoops! Something went wrong, please try again.']) //Send an error message
->withInput(request()->all()); //Send the inputs back
}
}
Document Model
public function storeNewDocument($file, $id, $type, $directory)
{
//Check the directory exists
if($this->checkDirectory($directory)){
//Get the file extention
$extension = $file->getClientOriginalExtension();
//Generate a new filename
$newName = md5(uniqid(rand(), true)) . "." . $extension;
//Move the file
try {
//Try to move the file
Storage::disk('local')->putFileAs($directory, $file, $newName);
//Create the new record
$document = new $this;
$document->type = $type;
$document->foreign_id = $id;
$document->nice_name = $file->getClientOriginalName();
$document->name = $nwName;
$document->save();
} catch (Exception $e){
dd($e);
//Log the error
Log::debug('Document Move Error', (Array) $e->getMessage());
//Try to delete the file incase the document save failed
Storage::delete($directory . '/' . $newName);
//Return false
return false;
}
//All moved, return the new name
//return $newName;
}
}
I have faked an error in the Document storeNewDocument function, by trying to call $nwName instead of $newName when saving the document to force an error.
What currently happens is
Case and member do not create (which is correct I believe because there is an error being thrown somewhere so the transaction doesn't complete?)
The file moves successfully in the Document storeNewDocument function, and lands in the storage/cases folder, which is correct.
The document then fails to save, but the dd($e) inside the catch on the document model is not hit?
At this point the document, case and member DB records have not saved, but the catch doesn't fire at all in the document model, so I cannot find the file and delete it?
I'm really not sure on how nested try/catch statements work. Can someone please let me know why the catch on my Document Model is not firing or if i'm trying to achieve my goal in a completely stupid way?!
Any sort of clarification would be massively appreciated!
Note - The $this->checkDirectory() function simply just checks if the function exists and create it if it doesn't, there is no issue with this function, just didn't see the point in adding it in to the question.
update - The error seems to be whenever an error is thrown inside the document model functions, it always hits the catch in the Case store function first. For example, if I got rid of the dd($e) function in the catch, in my log file, the log message is not Document Move Error, it is always Cases Create Error, which to me says that the document storeNewDocument catch is never being hit?

I think you just need to rethrow your exception.
} catch (Exception $e){
//Log the error
Log::debug('Document Move Error', (Array) $e->getMessage());
//Try to delete the file incase the document save failed
Storage::delete($directory . '/' . $newName);
//throw $e
throw $e;
}
You are returning false from this catch block, which is inside another try/catch block.

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

Catch FatalErrorException PHP

Hi everyone! In my Laravel application I have a upload function for an Excel file. I got this code from the web and adjusted it to my application. The problem is, it doesn't catch a fatal error exception which is produced when the user submits a file, but hasn't selected a file. I don't understand why it is not being caught. I will add a part of my controller.
public function upload() {
$file = array('thefile' => Input::file('thefile'));
$rules = array('excel' => 'excel');
$validator = Validator::make($file, $rules);
if ($validator->fails()) {
return Redirect::to('UploadExcelFile')->withInput()->withErrors($validator);
}
else {
try{
// FatalErrorException happens in this line!
if (Input::file('thefile')->isValid()) {
$destinationPath = 'uploads';
$fileName = Input::file('thefile')->getClientOriginalName();
Input::file('thefile')->move($destinationPath, $fileName);
Session::flash('success', 'Upload successfully');
$fileNameJSON = exec("python /path/to/script/ExcelToJSON3.py $fileName"); // This returns a full path name of the JSON file that is made...
if ($fileNameJSON == null){
return Redirect::to('/dashboard/input');
}
else {
// Getting the ID from the file that is uploaded
try {
$jsonDecode = json_decode(file_get_contents($fileNameJSON));
} catch (ErrorException $e){
return Redirect::to('/errorpage')->with(array('status'=> 'ErrorException'));
}
A lot of code for handling the data entered....
}catch (FatalErrorException $e){
return Redirect::to('/errorpage')->with(array('status'=> 'FatalErrorException'));
}
}
return true;
}
The error that is given:
FatalErrorException in UploadExcelFileController.php line 35:
Call to a member function isValid() on a non-object
So, I don't understand why this code doesn't handle the error exception and how I could fix this!
Unless you've imported the namespace that FatalErrorException is declared in with "use" you will need to scope the exception, like this:
catch (\Symfony\Component\Debug\Exception\FatalErrorException $e) {
Otherwise you're using whatever namespace your class is in and trying to catch an exception that is declared in that namespace. It looks like your ErrorException is similarly set up.
I'm not sure that the namespace above is the one your class is deriving from, I'm just guessing it is because you're using Laravel.

How to catch exceptions in entity?

I have a method in my entity with #ORM\PostRemove() which removes an associated file.
I wonder if I should do something like this:
try {
unlink($file);
} catch (\Exception $e) {
// Nothing here?
}
Does it make sense to catch an exception and do nothing in catch block? Or maybe I shouldn't catch exception here, but then, where should I do it? Should it be an exception from LifecycleCallback method?
I've read here that I shouldn't use logger in entity, so I'm confused what to put there instead.
Your entity shouldn't really contain business logic for your application, its purpose is to map objects to the database records.
The way to approach this depends on the application, for example if you have a File Controller and a removeAction within then the best place to remove the file would likely be here.
As an example: (psuedo code)
public function removeAction($id) {
$em = $this->getDoctrine()->getEntityManager();
$file = $em->getRepository('FileBundle:File')->find($id);
if (!$file) {
throw $this->createNotFoundException('No file found for id '.$id);
}
$filePath = $file->getPath();
if (file_exists($filePath) {
try {
unlink($filePath);
}
catch(Exception $e) {
// log it / email developers etc
}
}
$em->remove($file);
$em->flush();
}
You should always add error checking and reporting in your application, check that a file exists before you attempt to remove it.

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.

How to get a message in a variable if the page contains an error?

While we run an web application some page may contain error and some page maynot contain error,
I want to get a notification if the page contains some error ,If there is an error we can see the error in the page, but can we set any value to a variable if the page contains error.. such that we can get the notification that there is an error .
I want to get the notification since i want to create an error log,If we can set the variable with some value then we can use some condition to create a logfile
How can we do that?
There is several ways to do it. One is to setup a custom error handler. PHP will trap most errors raised during script execution and pass it to your custom handler then. What you do inside the handler is up to you. You can write to a log and then redirect to somewhere else or whatever you want.
If you are talking about Exceptions, then wrap code that can break in try/catch blocks. If an error occurs, handle the exception the catch block. What you put in there is again up to you.
Go through the linked pages to learn how this works. Catching an error, setting a variable and writing to a log are three distinct things. Isolate and solve them one by one.
You could also consider using a try { } catch { } block and writing exceptions to error log in catch { } part. Like this:
try {
$db = new MyDb('127.0.0.1', 'root', 'root');
if (false === $db) {
throw new Exception ('Could not connect to the database.');
}
$row = $db->getTable('table_name')->getRowByColumn('id', $_GET['id']);
if (null === $row) {
throw new Exception ('Row with id ' . $_GET['id'] . ' not found.')
}
// and so on
} catch (Exception $e) {
$fp = fopen('logs/error.txt', 'w');
fwrite($fp, date('l jS \of F Y h:i:s A') . ': ' . $e->getMessage() . "\n");
fclose($fp);
}
You get the idea.
Instead of just a date of error you could also append a login of signed in user if the script is in authentication protected area so you know which user got that error.

Categories