How to manually catch error exception in Laravel - php

In my project i need to do a bulk import and data insertion in the database.
So, I needed to know that when a API request is failed. Here, the problem is that PHP unable to catch that exception because Laravel 5.6 would stop the execution while there is any kind of error.
I needed to stop laravel from automatically stop the execution and let php decide that if an API request failed then lets wait 5 second and try again.
To achieve this i have made a function inside a laravel controller.
private function fetchAPI($id) {
try {
$rawResult = file_get_contents('http://example.com/'.$id.'?key=5453');
} catch (Exception $e) {
sleep(5);
$this->fetchAPI($id);
}
return json_decode($rawResult, true);
}
The above method will utilize the try...catch block. But i have also implemented with a boolean check with no luck:
private function fetchAPI($id) {
$rawResult = file_get_contents('http://example.com/'.$id.'?key=5453');
if($rawResult === FALSE) {
sleep(5);
$this->fetchAPI($id);
} else {
return json_decode($rawResult, true);
}
}
In this scenario how i can re-try if API request failed from a Laravel controller method?

Use \Exception dans not Exception, because Exception is thought as YourCurrentFileNamespace\Exception.

Related

Laravel try / catch on not working

I've searched a few questions for a reason my code is not throwing an error correctly, but I can't figure it out.
I have the following function in my controller
<?php
public function suspend($id)
{
try {
$this->collection = $this->class::find($id);
$this->collection->delete();
return $this->respond_with_success();
} catch (\Exception $e) {
return $this->respond_with_error('Failed to suspend resource with id: ' . $id);
}
}
For reference, I'm using soft deletes. I can suspend a resource once no problem. If I try to suspend one that's already suspended, Laravel correctly throws a 500 as I can see in the log file /storage/logs/laravel.log
This is part of the error I see;
local.ERROR: Call to a member function delete() on null....
Without using
withTrashed() in the query, a row quite obviously cannot be found. So this makes sense.
Great...so why does my catch not actually catch anything? I see a 500 error in the browser, but my application should allow me to continue and handle that error correctly. But it just falls over completely...
The respond_with_error function is below. I've tried changing the $code to 200 in testing, but this doesn't change anything. I've tested returning a simple string rather than with this function to no avail, so I don't think there's anything wrong with this part.
<?php
protected function respond_with_error($message = 'error', $code = 500)
{
return Response::json([
'success' => false,
'message' => $message,
], $code);
}
I'm running Laravel 5.6.29
There are two ways to address this. The first thing to note is ERROR: Call to a member function delete() on null is not an exception, it is a fatal error.
You can use findOrFail instead of find to throw an Exception when the model is not found and that will work.
You could also catch Throwable instead of Exception to catch errors and exceptions (as of PHP7) or just Error to catch errors.
As the Error hierarchy does not inherit from Exception, code that uses catch (Exception $e) { ... } blocks to handle uncaught exceptions in PHP 5 will find that these Errors are not caught by these blocks. Either a catch (Error $e) { ... } block or a set_exception_handler() handler is required.
Read more on PHP7 Error Handling here: http://php.net/manual/en/language.errors.php7.php

How do I do additional tests after expected exceptions are thrown?

I need to verify that the object's state is not affected by a mutator call that is expected to generate an error. I thought I could do:
function testWhatever(){
try{
// setup $obj
$this->expectException(WhateverException::CLASS);
// code that throws
}finally{
// assert that $obj's state is still valid
}
}
But I immediately realized it would certainly create some issues with PHPUnit (and it indeed does) if the code within finally also throws for whatever reason.
What are my options to perform some assertions after the exception has thrown, possibly without being too acrobatic with try/catch/rethrow and without repeating "code that throws" twice? Something with equivalent functionality to the following, which is not allowed:
function testWhatever(){
try{
// setup $obj
$this->expectException(WhateverException::CLASS);
// code that throws
}finally{
$this->expectException(null);
// assert that $obj's state is still valid
}
}
You can just use an ordinary try / catch / finally
function testFoobar()
{
// setup code
try {
// code that throws
$this->fail("Code didn't throw!");
} catch (ExpectedException $e) {
// verify exception message
} finally {
// verify other things
}
}
You could use two tests and make them depend on each other (just make your first test succeed on Exception
/**
* #depends testWhatever
*/
public function testSomethingAfterException() {
// more tests to run
}

Implementing a try/catch block in php constructor function

I have a query with try-catch block. I am going to create object and if it fails it will throw error message or say redirect on some other page.
Code
function __construct() {
try{
$this->ServiceObj = AnyClass::getService('XXX');
}
catch{
return Redirect::to('/');
}
}
public function MyOwnFunction() {
$getValueofCode = $this->_ServiceObj->set($endPoint_Url); //it's only example method not want to set anything
}
Is this correct way to define and use the try catch block in a constructor? Can I use try & catch block in construction? If NO than is there a better way to achieve this?
Thanks in Advance!!!
You can definitely use try catch in a constructor, just don't forget to specify the exception class you want to catch like catch (\Exception $e). However Laravel will not process the returned redirect. (By definition constructors shouldn't even return something)
An easy fix for this would be calling send() on the redirect. This will return it immediately to the client and stop the app.
try{
$this->ServiceObj = AnyClass::getService('XXX');
}
catch(\Exception $e){
Redirect::to('/')->send();
}
But since you mentioned a login page, you might be better of using a before filter to check and redirect. Like the built in auth filter does.
A third solution would be App::error. In app/start/global.php you could do something like this:
App::error(function(FooBarException $exception)
{
return Redirect::to('/');
});
Of course this only works if your exception is specific enough (replace FooBarException with the exception you want to catch)

Laravel catch exception and add message to a messagebag

I have a repository that throws an exception if it can't find a record in the database. Rather than redirect to another page I just want to display a warning alert as the record is not critical to the page but is an "exceptional event".
It's probably best to demonstrate with code:
// FxRateRepositoy
public function getRate(/** args **/)
{
$rate = FxRate::where(.... //query to get the rate
if (!rate)
throw new NonExistentCurrencyException(//message);
return $rate;
}
In my start/global.php I have a handler:
App::error(function(NonExistentCurrencyException $e)
{
Session::flash('alert', $e->getMessage());
return \\ ??
});
What to return? I must return a response or the exception continue uncaught. I want to continue to the intended page but with the alert flashed in the session. Is this possible without having to use try catch blocks in every place this method is called?
Ass an additional question, assuming this exception may be thrown multiple times in one request, what's the best way to accumulate alert messages and display them? I'm thinking something akin to the validation messageBag. Can I just use the global $errors variable or should I create a new, specific messagebag for this purpose?
The problem is that if you return nothing from App::error Laravel will display it's default error page. On the other side you can't return a response because you don't know what response it should be in the error handler.
I suggest you handle it in the controller itself.
You can catch the exception there and flash the message or don't throw an exception at all:
$rate = FxRate::where(.... //query to get the rate
if (!rate){
Session::flash('alert', 'Whoops');
}
Also the findOrFail() and firstOrFail methods might be of use. They throw an ModelNotFoundException if the query yields no results:
try {
$rate = FxRate::where(....)->firstOrFail()
// and so on
} catch (Illuminate\Database\Eloquent\ModelNotFoundException $e){
Session::flash('alert', 'Whoops');
}
As for a messages system, take a look at the laracasts/flash package

Symfony SncRedis Bundle exception handling if redis not available

I am using redis as a cache to help with application performance regarding data that has a high cost of generation. Currently we only have one redis instance running and I have observed that if redis is not available - then 500 error is being returned.
Given that I am caching - I would rather continue processing if redis is down and render that page with the data omitted.
I have tested using a basic php try - catch block - but it I have not been able to trap the exception.
public function redisAction()
{
try {
$redis = $this->container->get('snc_redis.default');
$val = $redis->get('foo:bar');
} catch (Exception $e ) {
$response = new Response('Oops ');
return $response;
}
....
}
Is there any other approaches I can take - I am also investigating the sncredis bundle to see if it can be addressed there.
Given you did not include use Exception on top of your class you will need to add a leading backslash ... otherwise PHP will try to catch Your\Current\Namespace\Exception which doesn't exist.
catch (\Exception $e ) {
// ...
}
But I'm not sure if redis throws an exception if the requested key does not exist (actually I don't think so).
You could use the exists() method to check if the key exists instead.
if ($redis->exists("foo:bar")) {
return new Response('Oops');
}

Categories