Catch errors with Laravel API Resource - php

I'm trying to use Laravel API Resource and handle the error message by sending a specific HTTP code
Here is my code :
public function show($id)
{
try {
return FruitResource::make(Fruit::find($id));
}
catch(Exception $e)
{
throw new HttpException(500, 'My custom error message');
}
}
My try/catch is systematically ignored when I try to access the route.
I am voluntarily accessing an object that is not in the database. I have ErrorException with message Trying to get property 'id' of non-object.
I would like to be able to send my own Exception here, in case the user tries to access data that doesn't exist. And return a json error.

Try this (notice the \ before Exception):
public function show($id)
{
try {
return FruitResource::make(Fruit::find($id));
}
catch(\Exception $e)
{
throw new HttpException(500, 'My custom error message');
}
}

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

Laravel throw ValidationException but not hitting destroy method's catch block?

I am trying to return a custom exception message using Laravel's ValidationException class. I have it successfully working in the following example:
public function store(Request $request)
{
$this->validate($request, CurrencyValidatorArrays::$store);
try {
$this->currenciesInstance->createOrUpdateCurrency($request->all());
return redirect()->route('currencies.index')
->with('success', 'Successfully created currency');
} catch (Exception $e) {
return redirect()->route('currencies.create')
->with('error', $e->getMessage());
}
}
where the exception is thrown from within createOrUpdateCurrency()
if(Currency::where('position', $data['position'])->where('id', '!=', $id)->exists()) {
throw ValidationException::withMessages([
'error' => ['Position value is already taken']
]);
}
This then passes the exception message back to my view for display.
However, I am trying to implement this somewhere else when trying to delete a company I am checking that no users exist first:
public function destroy($id)
{
try {
$this->companiesInstance->deleteCompany($id);
return redirect()->route('companies.index')
->with('success', 'Successfully deleted company');
} catch (Exception $e) {
return redirect()->route('companies.index')
->with('error', $e->getMessage());
}
}
Inside of deleteCompany():
$company = Company::find($id);
if($company->users()->exists()){
throw ValidationException::withMessages([
'error' => ['Position value is already taken']
]);
}
For some reason this doesn't hit my catch block from the main destroy method,if I change the exception type to ValidationException from Exception I can access and see the exception object but not in the way I can in the Store() methods version. Any ideas what is going on here?
UPDATE:
So I have a some what of a workaround:
To get by the exception type issue I used:
catch (ValidationException | Exception $e) { ...
But that still does not help when accessing the exception messages as when it's of type ValidationException the default constructor validation message is returned from getMessage() and not my specified one.
PHP 7 handles exceptions a bit differently. You can read it here.
Coming back to your code, you have to try and catch object of Throwable instead of Exception.
try
{
// Code that may throw an Exception or Error.
}
catch (Throwable $t)
{
// Code that handles the error
}
Hope this helps.

Catching an exception to throw another one, not working as expected

I have something like this in a Symfony controller:
try {
$entity = $this->getDoctrine()->getRepository(Entity::class)->findOneBy(['uuid' => $uuid]);
} catch(\Exception $ex) {
// die('Invalid uuid'); <- this works
throw $this->createNotFoundException('Resource not found...'); // this doesn't work
}
if(!$campaign) {
// this works if the uuid has a valid format but doesn't exist in db
throw $this->createNotFoundException($this->get('translator')->trans(Utils::NOT_FOUND_STRING));
}
I want to show an error 404 and not a 500 if the user opens a page with an invalid UUID.
The exception is caught, because if I put a die() it works. But with throw $this->createNotFoundException(, I get:
ConversionException
Could not convert database value "55012772-4801-4e26-8..." to Doctrine Type uuid_binary

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;

Categories