Skip to content

Latest commit

 

History

History
200 lines (160 loc) · 5.82 KB

4.Exceptions.md

File metadata and controls

200 lines (160 loc) · 5.82 KB

4. Exceptions

4.1 Basic PHP Exceptions

Read this answer on StackOverflow to understand the different exceptions PHP has.

4.2 Basic Laravel Exceptions

4.2.1 Pre-Defined

4.2.1.1 Docs

4.2.1.2 Types

4.2.2 Custom Exceptions

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';
}

4.3 Handling Exceptions

Understanding Handler's render() and report() method.

  1. render() - Used for returning as a response to requests.
  2. report() - Used for logging

4.3.1 The Switch Case Approach

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

4.3.2 Separating Web & API Requests (5.7 and lower)

  1. Make sure you have a Macro defined for Responses. Follow this - Link to Creating Response Macros

  2. Create a trait in app/Traits called RestExceptionHandlerTrait.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]);
            }
        }
    }
  3. 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/*');
        }
    }
  4. Now, in app/Exception/Handler.php, add the above 2 traits.

    class Handler extends ExceptionHandler
    {
        use RestTrait, RestExceptionHandlerTrait;
    }
  5. 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 to renderForWeb and modified as per requirements (as specified in 3.2.).