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 Answers
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));
});
resources/views/errors/404.blade.php
. Or do you have further conditional logic that needs implemented? – DigitalDrifter