Laravel: resource not found exception - laravel-5

Say you have a simple resource route like this:
Route::resource('overview', 'OverviewController');
And hit routes which you know don't exist. For example:
/overview/sdflkjdsflkjsd
/overview/sdflkjdsflkjsd/edit
Which in my case throws Trying to get property of non-object error from my view (as no resource is found)
I looked into adding 'Regular Expression Parameter Constraints' from the docs, but it looks like these are not available for resource routes either (plus don't really fix the problem).
I'm looking for a way to throw a single exception for this kind of thing, which I can then handle once, rather than add logic to each action (or at least the show and edit actions).. if possible.
EDIT After looking around github, I found the exception in the Symphony repo here. Is there a way I can hook into it?

Since you're getting a Trying to get property of non-object error, I assume you're fetching the resource via YourModel::find();
I'd suggest you use YourModel::findOrFail() instead. Then, you'd be getting a specific type of exception called ModelNotFoundException. Just register an error handler for this.
For instance,
App::error(function(ModelNotFoundException $e)
{
return Response::make('Not Found', 404);
});
UPDATE: This would actually go into render() method inside the app/Exceptions/Handler.php file in Laravel 5.1, and of course the code would utilize the passed $e parameter instead.
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException)
{
return \Response::make('Not Found', 404);
}
return parent::render($request, $e);
}

Related

Laravel: Way to diagnose "uncaught reflection: class log does not exist" without guessing?

As we all painfully know, this message is generated when an error occurs before Laravel has had a chance to instantiate a "Log" class instance to handle it. And... it therefore seems to completely conceal just what the underlying error is!
In my case the php artisan command won't run either.
Is there any way to find out what's wrong without "blind guessing?"
When you want to handle exceptions, a good way to catch it would be to implement a way to trapping exception out of controllers or services. You can do it in the "render" method of the App\Exceptions\Handler class. In this "render" method, you can write a block of "if" code to show the messages generated by an exception when Laravel throws an exception. For example:
public function render($request, Exception $exception)
{
if($exception) {
// do something
return response()->json(['error' => $exception->getMessage(),
$exception->getTraceAsString()], 500);
}
// Or if you created an exception specialization
if ($exception instanceof MyCustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
This was indeed caused by a syntax error, and I'm really surprised that Laravel had even managed to "get started" by that point in time. I literally found it by looking with git at a list of files that had recently changed.

Method render() is not being called when custom Laravel exception is thrown from view composer

Edited:
I have a custom exception with render method which is being called when I throw it e.g. from controller, but not being called when I throw it in View composer.
So when I do something like that
public function compose(View $view)
{
throw new CustomException();
}
and put dd() to exception render method
public function render()
{
dd('render is called');
}
I get no result.
If I log my exception directly, finds out that first the CustomException being thrown, then as the result I see ErrorException.
I found a place where it being thrown.
\Illuminate\View\Engines\CompilerEngine::handleViewException
protected function handleViewException(Exception $e, $obLevel)
{
$e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e);
parent::handleViewException($e, $obLevel);
}
I didn't found any mentions in Laravel docs about that case.
I found a tread on github with the same issue: https://github.com/laravel/framework/issues/24658
So the question is, is this expected? Is there any adequate way to avoid this behaviour?
Edit
So, as you know, any exception during view compilation is intercepted and rethrown as ErrorException or as FatalThrowableError.
What you can do is intercept ErrorException and check if ($e->getPrevious() instanceof \CustomException) if so, you do your code, else, let the handler continue.
So I've found working solution for myself.
I've extended CompilerEngine and added additional processing in order to not throw ErrorException when I don't want to.
The important thing is - your resulting Exception must be inherited from ErrorException. Otherwise you will face multiple calls to \App\View\Engines\CompilerEngine::handleViewException which can break your logic and write multiple log entities to your log file.

Design 500 error page only for production

I'm looking for a modern (Laravel 5.4) way to display custom 500 error page only for HTTP (non ajax/fetch) response. I read some threads but each response looks like a trick or is outdated. There is probably something to modify in \App\Exceptions\Handler, but I did not find the "right way".
Is there a simple way to display a specific page on fatal error (uncatched, returning 500) in Laravel 5.4?
In other words, when I have a syntax error on one of my controller, it displays "Whoops something went wrong" with some HTML and 500 error code. I would like to display something else, with the same rules as default behavior (ideally only for HTML browser, not for ajax/fetch, etc.).
EDIT: only in production environment.
Laravel makes it easy to display custom error pages for various HTTP status codes. For example, if you wish to customize the error page for 404 HTTP status codes, create a resources/views/errors/404.blade.php. This file will be served on all 404 errors generated by your application. The views within this directory should be named to match the HTTP status code they correspond to. The HttpException instance raised by the abort function will be passed to the view as an $exception variable.
https://laravel.com/docs/5.4/errors#custom-http-error-pages
From the selected "best answer" of this thread: https://laracasts.com/discuss/channels/general-discussion/custom-error-page-er500
You could modify \App\Exceptions\Handler::render():
public function render($request, Exception $exception)
{
if (config('app.debug') && !$this->isHttpException($exception)) {
$exception = new \Symfony\Component\HttpKernel\Exception\HttpException(500);
}
return parent::render($request, $exception);
}
Your exception will be reported in the logs as usually, but woops page will be replaced by your 500.blade.php view.
Sometimes you have to catch the specific exception in order to render the error view. in Laravel 5.4 you can do this by editing the report() method in the App\Exceptions\Handler class
public function report(Exception $exception)
{
if ($exception instanceof CustomException) {
// here you can log the error and return the view, redirect, etc...
}
return parent::report($exception);
}

Laravel forward to 404 error page

I am using Laravel 5.0. Is there any way to forward to default 404 page error if the user wrong url. I want to forward all the time if the user entered wrong url in my project.
In your resources/views/errors folder make sure you have a 404.blade.php file and if that is not there then create it and put something in this file.
Basically, if that 404 file is not present there then You'll see an error like this:
Sorry, the page you are looking for could not be found.
NotFoundHttpException in Application.php line 901:
....
.
Sepending on your environment setup. FYI, in app\Exceptions\Handler.php file the handler method handles/catches the errors. So check it, you may customize it, for example:
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
// You can use/catch this but basically this is not included in Handler
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class Handler extends ExceptionHandler {
//...
public function render($request, Exception $e)
{
// Customize it, extra code
if ($e instanceof AccessDeniedHttpException) {
return response(view('errors.403'), 403);
}
// The method has only this line by default
return parent::render($request, $e);
}
}
Then, make sure, the 403.blade.php is also available in your resources/views/errors directory.

Laravel 4: redirect if item doesn't exists, ModelNotFoundException doesn't work anyway I try it

I'm following Dayle Rees' book "Code Bright" tutorial on building a basic app with Laravel (Playstation Game Collection).
So far so good, the app is working but, following his advices at the end of the chapter, I'm doing my homeworks trying to improve it
So, this snippet is working fine for existing models but throws an error if the item doesn't exists:
public function edit(Game $game){
return View::make('/games/edit', compact('game'));
}
In other words, http://laravel/games/edit/1 shows the item with ID = 1, but http://laravel/games/edit/21456 throws an error since there's no item with that ID
Let's improve this behaviour, adapting some scripts found also here on StackOverflow (Laravel 4: using controller to redirect page if post does not exist - tried but failed so far):
use Illuminate\Database\Eloquent\ModelNotFoundException; // top of the page
...
public function edit(Game $game){
try {
$current = Game::findOrFail($game->id);
return View::make('/games/edit', compact('game'));
} catch(ModelNotFoundException $e) {
return Redirect::action('GamesController#index');
}
}
Well... nothing happens! I still have the error with no redirect to the action 'GamesController#index'... and please notice that I have no namespaces in my Controller
I tried almost anything:
Replace catch(ModelNotFoundException $e) with catch(Illuminate\Database\Eloquent\ModelNotFoundException $e): no way
put use Illuminate\Database\Eloquent\ModelNotFoundException; in Model instead of Controller
Return a simple return 'fail'; instead of return Redirect::action('GamesController#index'); to see if the problem lies there
Put almost everywhere this snippet suggested in Laravel documentation
App::error(function(ModelNotFoundException $e)
{
return Response::make('Not Found', 404);
});
Well, simply nothing happened: my error is still there
Wanna see it? Here are the first two items in the errors stack:
http://www.iwstudio.it/laravelerrors/01.png
http://www.iwstudio.it/laravelerrors/02.png
Please, can someone tell me what am I missing? This is driving me mad...
Thanks in advance!
Here are few of my solutions:
First Solution
The most straightforward fix to your problem will be to use ->find() instead of ->findOrFail().
public function edit(Game $game){
// Using find will return NULL if not found instead of throwing exception
$current = Game::find($game->id);
// If NOT NULL, show view, ELSE Redirect away
return $current ? View::make('/games/edit', compact('game')) : Redirect::action('GamesController#index');
}
Second solution
As I notice you may have been using model binding to your route, according to Laravel Route model binding:
Note: If a matching model instance is not found in the database, a 404 error will be thrown.
So somewhere where you define the model binding, you can add your closure to handle the error:
Route::model('game', 'Game', function()
{
return Redirect::action('GamesController#index');
});
Third Solution
In your screenshot, your App::error seems to work as the error says HttpNotFound Exception which is Laravel's way of saying 404 error. So the last solution is to write your redirect there, though this apply globally (so highly discouraged).

Resources