How to catch exceptions in entity? - php

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.

Related

Nested Laravel Try catch not throwing inner Exception, only outer

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.

Exceptions handler

I have to develop an exception handler that should handle like 5 different type of exceptions. Let's call them simply Ex1, Ex2, Ex3...
I though of doing a single class called ExHandler which will be instantiated like this:
...
} catch (Ex1 $e) { $h = new ExHandler($e); $h->render(); }
catch (Ex2 $e) { $h = new ExHandler($e); $h->render(); }
catch (Ex3 $e) { $h = new ExHandler($e); $h->render(); }
...
And inside ExHandler manage each different Exception differently using $e instance of Ex1, $e instance of Ex2, $e instance of Ex3...
But It doesn't seems a very good practice to me. Is it good? Is there any other way of doing this?
Should I create an Ex1Handler, Ex2Handler, Ex3Handler...? My S.O.L.I.D spirit tells me something is just wrong here. What is it?
I need to note before I answer this, that procedural programmers will look at this and think it's dumb :) but I can live with that, this is assuming an OOP application with HTML templating that outputs after the output_buffer is cleaned.
I always create a try/catch block encompassing the majority of my code in one call usually at the point where I start requiring other files as well as starting an output_buffer whilst in development.
ob_start();
try {
switch($appPage) {
case('work'):
require_once('im_bored_at_work.php');
break;
case('home'):
require_once('im_a_little_less_bored_at_home.php');
break;
default:
require_once('on_the_fence.php');
}
} catch (Exception $e) {
// Handle exception caught and apply formatting
}
$devOut = ob_get_contents();
ob_end_flush();
To give an example how I would handle the multiple exceptions you need to catch with a custom class
class CustomExceptionHandler extends Exception {
private $msg;
private $code;
private $otherVars;
public function __construct($msg,$code=0,$otherDebugVar=null){
$this->msg = $msg != null ? $msg : "An unknown exception was thrown";
$this->code = $code;
$this->otherVars = $otherDebugVar;
parent::__construct($msg,$code);
}
public function getOtherVars() {
return $this->otherVars;
}
}
The idea is to just keep the custom information within the exception object, and when you rethrow the exception at the end of a try/catch block as a standard exception you include the formatted custom message, it shouldn't really matter now which Exception handler picked up the original exception as all the info you will need will come downstream and be caught in the original try / catch block.
class BasicTemplate {
private $template;
private $path;
private $contents;
public function __construct($template, $path) {
$this->template = $template;
$this->path = $path;
$this->buildTemplate();
}
private function buildTemplate() {
if ($contents = #file_get_contents($this->path . $this->template)) {
$this->contents = $contents;
} else {
$e = new CustomExceptionHandler("Message",2,$this->path . $this->template);
// Do whatever else you want to do with custom exception handling class
throw $e;
}
}
}
Now you need to catch your exception and rethrow it:
try {
$html = new BasicTemplate($temp,$path);
} catch {CustomExceptionHandler $e) {
throw new Exception("Message: {$e->getMessage()} Other Info: {$e->getOtherVars()}",$e->getCode());
}
That's the rough idea anyhow, hope it helps.

Simple Exception sample - PHP

I am trying to understand what the best approach would be to handle Exceptions in the following scenario:
I have a class employee:
class employee extends person {
private $salary;
private $baseSalary = 6.5;
function __construct($f, $m, $l, $a,$fsalary=0){
if(!is_numeric($fsalary)){
throw new Exception("Age supplied is not a number", 114);
}
parent::__construct($f, $m, $l, $a);
$this->salary=$fsalary;
}
function GetDetails(){
return parent::GetName().
"<br/>".
$this->salary;
}
function __toString(){
return $this->GetDetails();
}
}
And using this:
try{
if(!$f = new employee("Sarah", "Sebastian", "Pira", "abc")){
throw new Exception();
}
else {
echo $f;
}
}
catch (Exception $e){
echo "<br/>";
echo var_dump($e);
}
Now I would think it would be a good idea to throw an exception in the class and then use just one catch block in all the scripts that would be using an employee object - But this doesn't seem to work - I need to have a try catch block within the class - Is this the correct way of looking at this?
Thanks
I think what you're saying is that you want to do something like this:
try {
class Employee extends Person {
// ...blah blah...
}
}
catch(Exception $e) {
// handle exception
}
...and then be able to insantiate it in other classes, without explicitly catching any exceptions:
// try { << this would be removed
$employee = new Employee();
// }
// catch(Exception $e) {
// (a whole bunch of code to handle the exception here)
// }
You can't do that, because then the try/catch block in the class will only catch any exceptions that occur when defining the class. They won't be caught when you try to instantiate it because your new Employee line is outside the try/catch block.
So really, your problem is that you want to be able to re-use a try/catch block in multiple places without re-writing the code. In that case, your best solution is to move the contents of the catch block out to a separate function that you can call as necessary. Define the function in the Employee class file and call it like this:
try {
$employee = new Employee();
$employee->doSomeStuff();
$employee->doMoreStuffThatCouldThrowExceptions();
}
catch(Exception $e) {
handle_employee_exception($e);
}
It doesn't get rid of the try/catch block in every file, but it does mean that you don't have to duplicate the implementation of the exception-handling all the time. And don't define handle_employee_exception as an instance method of the class, do it as a separate function, otherwise it will cause a fatal error if the exception is thrown in the constructor because the variable won't exist.
You should read more about Exceptions in PHP.
You can handle exceptions within the methods of the class, sure. But you should rethink how you want to do this and... why.
Good practice is also creating own exception class, so you are able to distinguish exceptions thrown by your module / class from the exceptions thrown by something else. It looks like that (see more):
class EmployeeModule_Exception extends Exception {}
and when it comes to throwing exception:
// the second parameter below is error code
throw new EmployeeModule_Exception('some message', 123);
Catching is similar, only the below example will catch only your module's exceptions:
try {
// some code here
} catch (EmployeeModule_Exception $e) {
// display information about exception caught
echo 'Error message: ' . $e->getMessage() . '<br />';
echo 'Error code: ' . $e->getCode();
}

PHP Exceptions in Classes

I'm writing a web application (PHP) for my friend and have decided to use my limited OOP training from Java.
My question is what is the best way to note in my class/application that specific critical things failed without actually breaking my page.
My problem is I have an Object "SummerCamper" which takes a camper_id as it's argument to load all of the necessary data into the object from the database. Say someone specifies a camper_id in the query string that does not exist, I pass it to my objects constructor and the load fails. I don't currently see a way for me to just return false from the constructor.
I have read I could possibly do this with Exceptions, throwing an exception if no records are found in the database or if some sort of validation fails on input of the camper_id from the application etc.
However, I have not really found a great way to alert my program that the Object Load has failed. I tried returning false from within the CATCH but the Object still persists in my php page. I do understand I could put a variable $is_valid = false if the load fails and then check the Object using a get method but I think there may be better ways.
What is the best way of achieving the essential termination of an object if a load fails? Should I load data into the object from outside the constructor? Is there some sort of design pattern that I should look into?
Any help would be appreciated.
function __construct($camper_id){
try{
$query = "SELECT * FROM campers WHERE camper_id = $camper_id";
$getResults = mysql_query($query);
$records = mysql_num_rows($getResults);
if ($records != 1) {
throw new Exception('Camper ID not Found.');
}
while($row = mysql_fetch_array($getResults))
{
$this->camper_id = $row['camper_id'];
$this->first_name = $row['first_name'];
$this->last_name = $row['last_name'];
$this->grade = $row['grade'];
$this->camper_age = $row['camper_age'];
$this->camper_gender = $row['gender'];
$this->return_camper = $row['return_camper'];
}
}
catch(Exception $e){
return false;
}
}
A constructor in PHP will always return void. This
public function __construct()
{
return FALSE;
}
will not work. Throwing an Exception in the constructor
public function __construct($camperId)
{
if($camperId === 1) {
throw new Exception('ID 1 is not in database');
}
}
would terminate script execution unless you catch it somewhere
try {
$camper = new SummerCamper(1);
} catch(Exception $e) {
$camper = FALSE;
}
You could move the above code into a static method of SummerCamper to create instances of it instead of using the new keyword (which is common in Java I heard)
class SummerCamper
{
protected function __construct($camperId)
{
if($camperId === 1) {
throw new Exception('ID 1 is not in database');
}
}
public static function create($camperId)
{
$camper = FALSE;
try {
$camper = new self($camperId);
} catch(Exception $e) {
// uncomment if you want PHP to raise a Notice about it
// trigger_error($e->getMessage(), E_USER_NOTICE);
}
return $camper;
}
}
This way you could do
$camper = SummerCamper::create(1);
and get FALSE in $camper when the $camper_id does not exist. Since statics are considered harmful, you might want to use a Factory instead.
Another option would be to decouple the database access from the SummerCamper altogether. Basically, SummerCamper is an Entity that should only be concerned about SummerCamper things. If you give it knowledge how to persist itself, you are effectively creating an ActiveRecord or RowDataGateway. You could go with a DataMapper approach:
class SummerCamperMapper
{
public function findById($id)
{
$camper = FALSE;
$data = $this->dbAdapter->query('SELECT id, name FROM campers where ?', $id);
if($data) {
$camper = new SummerCamper($data);
}
return $camper;
}
}
and your Entity
class SummerCamper
{
protected $id;
public function __construct(array $data)
{
$this->id = data['id'];
// other assignments
}
}
DataMapper is somewhat more complicated but it gives you decoupled code which is more maintainable and flexible in the end. Have a look around SO, there is a number of questions on these topics.
To add to the others' answers, keep in mind that you can throw different types of exceptions from a single method and handle them each differently:
try {
$camper = new SummerCamper($camper_id);
} catch (NoRecordsException $e) {
// handle no records
} catch (InvalidDataException $e) {
// handle invalid data
}
Throwing an exception from the constructor is probably the right approach. You can catch this in an appropriate place, and take the necessary action (e.g. display an error page). Since you didn't show any code, it's not clear where you were catching your exception or why that didn't seem to work.
try {
$camper = new SummerCamper($id);
$camper->display();
} catch (NonexistentCamper $ex) {
handleFailure($ex);
}

try catch statement in PHP where the file does not upload

I understand what try-catch statements do, but from reading the documentation on php.net, I would not be able to implement one into my own code. I need a real example to help me understand.
How can I turn this example into a try-catch statement, if the upload was not successful?
$move = move_uploaded_file($_FILES['file']['tmp_name'], $_SERVER['DOCUMENT_ROOT'].'/uploads/'.$_FILES['file']['name']);
if (!$move) {
die ('File didn\'t upload');
} else {
//opens the uploaded file for extraction
echo 'Upload Complete!';
}
This may not be a good example to work with, but any help would be appreciated.
You could do it like this.
try {
//throw exception if can't move the file
if (!move_uploaded_file( ... )) {
throw new Exception('Could not move file');
}
//do some more things with the file which may also throw an exception
//...
//ok if got here
echo "Upload Complete!";
} catch (Exception $e) {
die ('File did not upload: ' . $e->getMessage());
}
It is a bit pointless for the above example, but you should get the idea. Note that you can throw the exception(s) from anywhere (e.g. within a function/method that you call from withing the try{}) and they will propagate upwards.
Well, if you want to use exceptions, you could do something like:
function handleUpload() {
$move = move_uploaded_file($_FILES['file']['tmp_name'], $_SERVER['DOCUMENT_ROOT']."/uploads/".$_FILES['file']['name']);
if (!$move) {
throw new Exception('File Didnt Upload');
}
}
try {
handleUpload();
echo "File Uploaded Successfully";
} catch(Exception $ex) {
die($ex->getMessage);
}
I know this may seem like bloat - but you can call the method from anywhere in the call stack, and catch the exception at any point.
try-catch statements are used to handle exceptions. I don't believe that the function move_uploaded_files can throw and exception, as such I think that you code is written is correct. After the call, you look at the return code. If it's false, you end processing, otherwise you are report success.
According to a similar post in PHPbug, only OO code (Object-Oriented code) throws exceptions. That would mean that functions such as move_uploaded_file won't throw their own exceptions, but some other code will.

Categories