Laravel 5 - try catch exception not working - php

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.

Related

How to return custom response for `Illuminate\Support\ItemNotFoundException`?

In my controller, I used the method firstOrFail() for a database query with Eloquent. When in debug mode, I get a Laravel error message with the content Illuminate\Support\ItemNotFoundException.
However, I'd like to return a redirect() or mabye back() instead of showing this error screen. How can I do that?
use php try catch, to override the behaviour of error catching,
try {
// your functional code
} catch (Illuminate\Support\ItemNotFoundException $exception ) {
//your redirect command , the code here will be executed if there is an exception with type ItemNotFoundException
}
if you want to catch all errors, you need to use the General Exception class which is the parent of all Exceptions
try {
// your functional code
} catch (\Exception $exception) {
//your redirect command , the code here will be executed if there is any exception
}
if you want to get the exception message to log it or any customization , you can use the method : getMessage() , in our case will be $exception->getMessage()
Instead of firstOrFail() use first() and use condition to redirect back, for example:
$item = Item::where('slug', $slug)->first();
if (! $item) {
return redirect()->back();
}

Is try catch block necessary inside controller in laravel?

I'm currently working on a project with my friend. He follows this method of writing codes. Is it good or bad? I'm also not that much experience developer.
public function store(Request $request)
{
try
{
$comment = new TicketComment();
$comment->content = $request['comment'];
$comment->user_id = Auth::user()->id;
$comment->ticket_id = $request['ticketId'];
$comment->save();
$ticket = Ticket::where('id', '=', $comment->ticket_id)->first();
$ticket->updated_at = $comment->created_at;
$ticket->update();
}
catch(Exception $e)
{
request()->session()->flash('unsuccessMessage', 'Failed to add comment !!!');
return redirect()->back();
}
request()->session()->flash('successMessage', 'Comment has been successfully added !!!');
return redirect()->back();
}
Don't have a broad Exception catch block. Only catch exceptions you expect to be thrown in that block that way you can properly log unexpected exceptions and fix any other bugs in your code that may have caused those, instead of hiding them from yourself.
If you must do this then it might be in the context of something like:
public function store(Request $request)
{
try
{
$comment = new TicketComment();
$comment->content = $request['comment'];
$comment->user_id = Auth::user()->id;
$comment->ticket_id = $request['ticketId'];
$comment->save();
$ticket = Ticket::where('id', '=', $comment->ticket_id)->first();
$ticket->updated_at = $comment->created_at;
$ticket->update();
}
catch(Exception $e)
{
if (!($e instanceof SQLException)) {
app()->make(\App\Exceptions\Handler::class)->report($e); // Report the exception if you don't know what actually caused it
}
request()->session()->flash('unsuccessMessage', 'Failed to add comment !!!');
return redirect()->back();
}
request()->session()->flash('successMessage', 'Comment has been successfully added !!!');
return redirect()->back();
}
This way any unexpected exceptions will still be reported and you can go through your logs later to fix any bugs that are causing them.
As a reminder since PHP 7.1 you can have a union of exceptions in a catch block (Reference) e.g.
try { }
catch (ExceptionType1|ExceptionType2 $e) {
}
This way you can handle the exceptions you know you can handle and let Laravel handle the ones you are not sure how to handle.
Note that you generally don't have to have try-catch blocks in controller code, you can always use the exception handler to do the flash/redirect back combination for all unhandled exceptions if that is your preferred way of handling them.
It is allways good to handle the errors. However, Laravel has built in error handling, which ease this process. This does not mean you dont need the try catch block.
Read more about laravel error handling: https://laravel.com/docs/5.8/errors
Even that laravel has his own built in error handling you better use try & catch.
Don't use it on development mode but on production mode. The use of try & catch is user friendly, which means that user will not see the traditional page with the error generated from laravel (in case of any error), but will see your default message.
Example of how to use it:
public function index()
{
try {
return view('index.blade.php');
} catch (\Exception $exception) {
\Log::error($exception);
return redirect()->back()->with(['message' => 'There was an error']);
}
}
If you see this part \Log::error($exception);, it means that the error generated from laravel will be registered on logs file under storage/logs/laravel.log so you as e developer can see the problem with that method. On the other part if something goes wrong with accessing the page: view('index.blade.php') the user will be redirected back to the previous page with a message saved in session, which is: 'There was an error'.
To catch the message in case of errors in your blade you should:
#if (session('message'))
<div class="container">
<div class="alert alert-danger text-center">{{ session('message') }}</div>
</div>
#endif
Happy coding!
here is how I handled the same. I logged the error instead of returning it to user interface.
public function postData()
{
try
{
//your logics to post goes here
}
catch (\Exception $exception) {
if (!($exception instanceof \SQLiteException)) {
app()->make(\App\Exceptions\Handler::class)->report($exception); // Report the exception if you don't know what actually caused it
}
Alert::toast('Error Toast', 'An error occurred');
\Log::error($exception);
return redirect()->back();
}
Alert::success('SuccessAlert', 'alert');
return back();
}

Prevent laravel message to be displayed and redirect the user to custom page issue in Laravel 5

I would like to catch somehow the laravel error, warning message. I don't want to disable them from the config/app.php file. I am using monolog to log some information.
This is my piece of code:
public function view($id){
try {
$tag = Tags::find(12313); // tags is a model
}catch(Exception $error){
echo 'error'; exit();
$this->log->logMessage(Logger::ERROR, $error->getMessage());
return redirect()->route('admin.tags')->with(['msg' => 'Smth went wrong']);
}
}
$this->log is a class where I am using the monolog class to log information.
The fact is that right now , it doesn't go to the catch part . I don't get the error message. I'm getting this message from laravel:
Trying to get property of non-object (View: ......
I intentionally put the number 12313 there to see if it is working or not. And for some reason is not working and I am not redirected . The idea, if something happened I want to redirect the user to a specific page with a general error message. How can I achieve that ?
You can do it in laravel .You can handle erors in App\Exceptions\Handler class
public function render($request, Exception $exception)
{
if($exception instanceof NotFoundHttpException)
{
return response()->view('errors.404', [], 404);
}
if ($exception instanceof MethodNotAllowedHttpException)
{
return response()->view('errors.405', [], 405);
}
if($exception instanceof MethodNotAllowedHttpException)
{
return response()->view('errors.404', [], 405);
}
return parent::render($request, $exception);
}
find() method doesn't throw an exception if the record is not found. So do this instead:
public function view($id)
{
$tag = Tags::find(12313); // tags is a model
if (is_null($tag)) {
$this->log->logMessage(Logger::ERROR, $error->getMessage());
return redirect()->route('admin.tags')->with(['msg' => 'Smth went wrong']);
}
}
Or use findOrFail() which will throw an exception if specified record is not found.
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

Laravel : Handle findOrFail( ) on Fail

I am looking for something which can be like findOrDo(). Like do this when data not found. Something could be like
Model::findOrDo($id,function(){
return "Data not found";
});
Is there any similar thing in laravel that I can do this elegantly and beautifully ?
*I tried googling but could not find one
use Illuminate\Database\Eloquent\ModelNotFoundException;
// Will return a ModelNotFoundException if no user with that id
try
{
$user = User::findOrFail($id);
}
// catch(Exception $e) catch any exception
catch(ModelNotFoundException $e)
{
dd(get_class_methods($e)); // lists all available methods for exception object
dd($e);
}
Another option is to modify the default Laravel Exception Handler, found in app/Exceptions/Handler.php on the render() function I made this change:
public function render($request, Exception $e)
{
if(get_class($e) == "Illuminate\Database\Eloquent\ModelNotFoundException") {
return (new Response('Model not found', 400));
}
return parent::render($request, $e);
}
That way instead of getting a 500, I send back a 400 with a custom message without having to do a try catch on every single findOrFail()
By default, when you use an Eloquent model’s findOrFail in a Laravel 5 application and it fails, it returns the following error:
ModelNotFoundException in Builder.php line 129:
'No query results for model [App\Model]'.
So to catch the exception and display a custom 404 page with your error message like "Ooops"....
Open up the app/Exceptions/Handler.php file, and add the code shown below to the top of the render function:
public function render($request, Exception $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException)
{
abort(404, 'Oops...Not found!');
}
return parent::render($request, $e);
}
Source: https://selftaughtcoders.com/from-idea-to-launch/lesson-16/laravel-5-findorfail-modelnotfoundexception-show-404-error-page/
An alternative process could be to evaluate a collection instead. So,
$modelCollection = Model::where('id', $id)->get();
if(!$modelCollection->isEmpty()) {
doActions();
}
I agree it isn't as elegant, a one-liner or as case specific as you or I might like, but aside from writing a try catch statement every time, it's a nice alternative.
as of Laravel v5.7, you can do this (the retrieving single model variation of #thewizardguy answer)
// $model will be null if not found
$model = Model::where('id', $id)->first();
if($model) {
doActions();
}
A little later for the party, from laravel 5.4 onward, Eloquent Builder supports macros. So, I would write a macro (in a separate provider) like follows.
Builder::macro('firstOrElse', function($callback) {
$res = $this->get();
if($res->isEmpty()) {
$callback->call($this);
}
return $res->first();
});
I can then do a retrieval as follows.
$firstMatchingStudent = DB::table('students')->where('name', $name)
->firstOrElse(function() use ($name) {
throw new ModelNotFoundException("No student was found by the name $name");
});
This will assign the first object of the result set if it is not empty, otherwise will adapt the behaviour I pass into the macro as a closure (throw Illuminate\Database\Eloquent\ModelNotFoundException in this case).
For the case of a model also this would work, except that models always return the builder instance.
In the newer version of Laravel (for me in v9.41.0) we have the Register method in Handler.php ,
so for customize the exception of findOrFail method we have to add this code to that file:
$this->renderable(function (NotFoundHttpException $exception, $request) {
if ($request->expectsJson()) {
//when you need it for API
return response()->json([
'responseCode'=> 404,
'message' => "Item not found",
'errorCode' => 1000404
], 404 );
}else{
return view("some-custom-view");
}
});
I needed NotFoundHttpException which has the namespace below (you can use any other Exception for customization) and have to be used out of the class:
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

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
}

Categories