0
votes

I'm handling laravel exceptions such as NotFoundHttpException and UnauthorizedException by type-checking exceptions in the exception handler's render method, making a lot of instanceof checks which in my opinion violates OCP (open for extension closed for modification) principle. The documentaion solves this by using renderable exceptions (https://laravel.com/docs/6.x/errors#renderable-exceptions) that must be thrown from the controller (or any where) which I don't want, I want to catch laravel exceptions' such as ModelNotFoundException in the handler class and return my custom response in a clean way,in other words, I'm looking for a clean way to handle laravel exceptions without throwing the exception from the controller.

1
The framework gives you two ways of handling exceptions as you want, neither of which you're happy with it seems. Why is this level of fine tuned control needed? If you simply want to change whats displayed on the page when a given exception occurs, you can override the blade file by creating your own, such as resources/views/errors/404.blade.php. Or do you have further conditional logic that needs implemented?DigitalDrifter

1 Answers

0
votes

If it is about OCP only, you can use a chain of responsibility design pattern, you can create a class (which is open-for-extension closed-for-modifications), that has a "next" field, which is from the same class type, and make the conversions you want like that:

interface ErrorHandler {
    function handleError($request);
}

abstract class NodeInErrorHandlerChain {
    private $next; // It should have the same `NodeInErrorHandlerChain` type

    public NodeInErrorHandlerChain($next) {
        $this->next = $next;
    }

    abstract protected function currentHandler($exception, $request);

    public function handle($exception, $request) {
        $current = currentHandler($exception, $request);
        return $current != null ? $current : 
              ($next == null ? null : $next->handle($exception, $request));
    }
}

Then implement it as follows:

class ModelNotFoundNode extends NodeInErrorHandlerChain {
    protected function currentHandler($exception, $request) {
        if($exception instanceof ModelNotFoundException) {
            return ModelNotFoundHandler($request); // This should be easy to implement
        }
    }
}

So, now you check on ModelNotFoundException only, if you want to check on other types, create the ModelNotFoundNode with $next not equal to null, but rather equal to for example ValidationException (you would implement it the same way), so adding any other exception of those would be just making another class that extends NodeInErrorHandlerChain, and make it the $next to the previous last element of the chain, when you create the chain (for example in the Provider which provides dependency injection in your app).

    $this->app->singleton(NodeInErrorHandlerChain::class, function ($app) {
        return new ModelNotFoundNode(ValidationExceptionNode(null));
    });