Read this answer on StackOverflow to understand the different exceptions PHP has.
- 5.6 - https://laravel.com/docs/5.6/errors
- 5.7 - https://laravel.com/docs/5.7/errors
- 5.8 - https://laravel.com/docs/5.8/errors
- Http - https://laravel.com/api/5.7/Illuminate/Http/Exceptions.html
- Routing - https://laravel.com/api/5.7/Illuminate/Routing/Exceptions.html
- Eloquent - https://laravel.com/api/5.7/Illuminate/Database/Eloquent.html
- Authentication - https://laravel.com/api/5.7/Illuminate/Auth.html
- Validation - https://laravel.com/api/5.7/Illuminate/Validation/ValidationException.html
In Laravel, when creating exceptions, store in app/Exceptions/
. You can easily create exceptions using this command
php artisan make:exception PaymentException
Note: Please extend these exceptions with the correct base PHP exceptions to give it a default status code as well.
For eg. InvalidArgumentException can be used for exceptions like this,
class InvalidPayloadException extends \InvalidArgumentException
{
/**
* Exception Message
*
* @var string
*/
protected $message = 'Invalid Payload';
}
and UnexpectedValueException can be used for something like this
class PaymentException extends \UnexpectedValueException
{
/**
* Exception Message
*
* @var string
*/
protected $message = 'The payment failed';
}
Understanding Handler's render()
and report()
method.
render()
- Used for returning as a response to requests.report()
- Used for logging
It is important to take control over the type of response to be sent for some responses. The default responses sent by Laravel may not fit the client's requirements and situations may arrive when the client may ask for (let's say) a custom page to show when 404 occurs.
This can be done by simply adding this switch case to the render()
method in app/Exceptions/Handler.php
.
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
protected function render($request, Exception $e)
{
switch (true) {
case ($e instanceof ModelNotFoundException):
return redirect()->route('errors.404.get');
break;
default:
return parent::render($request, $e);
break;
}
}
-
Make sure you have a Macro defined for Responses. Follow this - Link to Creating Response Macros
-
Create a trait in
app/Traits
calledRestExceptionHandlerTrait.php
namespace App\Traits; use Exception; use Illuminate\Http\Request; use Illuminate\Auth\AuthenticationException; use Illuminate\Auth\Access\AuthorizationException; use Symfony\Component\Debug\Exception\FlattenException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use App\Exceptions\ValidationException as CustomValidationException; trait RestExceptionHandlerTrait { /** * Creates a new JSON response based on exception type. * * @param Request $request * @param Exception $e * @return \Illuminate\Http\JsonResponse */ protected function renderForApi(Request $request, Exception $e) { switch (true) { case ($e instanceof ModelNotFoundException): return $this->modelNotFound($e); case ($e instanceof AuthorizationException): return $this->authorization($e, $e->getMessage()); case ($e instanceof AuthenticationException): return $this->authentication($e, $e->getMessage()); case ($e instanceof CustomValidationException): return $this->validation($e); default: $e = FlattenException::create($e); $message = !empty($e->getMessage()) ? $e->getMessage() : array_get(SymfonyResponse::$statusTexts, $e->getStatusCode()); return $this->jsonResponse($e, ['message' => $message]); } } }
-
Create another trait called
RestTrait.php
namespace App\Traits; use Illuminate\Http\Request; trait RestTrait { /** * Determines if request is an api call. * * If the request URI contains 'api/'. * * @param Request $request * @return bool */ protected function isApiCall(Request $request) { return $request->is('api/*'); } }
-
Now, in
app/Exception/Handler.php
, add the above 2 traits.class Handler extends ExceptionHandler { use RestTrait, RestExceptionHandlerTrait; }
-
Now modify the
render()
function to this./** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $e * @return \Illuminate\Http\Response */ public function render($request, Exception $e) { if ($this->isApiCall($request)) { return $this->renderForApi($request, $e); } return $this->renderForWeb($request, $e); }
Take note of the
$this->renderForWeb($request, $e);
. The code in render function written before is transferred torenderForWeb
and modified as per requirements (as specified in 3.2.).