Home > OS >  custom exception of Laravel is not working as expected
custom exception of Laravel is not working as expected

Time:12-23

I am working with the Laravel 9 application. I have created a custom Exception. I want to report the General Exception to the sentry and this custom Exception to another vendor like Papertrail.

The Handler.php is not calling the reportable closure function when the application throws a custom exception i.e ServiceException.

By the way, the Laravel documentation for errors is also not understandable

Handler.php

<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Inertia\Inertia;
use Throwable;
use Exception;
use App\Exceptions\ServiceException;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        // ServiceException::class
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     *
     * @return void
     */
    public function register()
    {
        $this->reportable(function (Throwable $e) {
            // this block is calling for Exception but not for custom Exception like ServiceException.
            if ($this->shouldReport($e) && app()->bound('sentry')) {
                app('sentry')->captureException($e);
            }
        });

        $this->reportable(function (ServiceException $e) {
            // this block is not calling when I throw ServiceException.
            echo "Send this ServiceException to papertrail app."; // this line is never called.
            die;
        });
    }

    public function render($request, Throwable $e)
    {
        $response = parent::render($request, $e);
        if ($request->isJson()) {
            //prevent error for local and staging.
            if (! app()->environment(['local', 'staging']) && in_array($response->status(), [500, 503, 404, 403])) {
                \Log::error('API Error Handler', [$response->getOriginalContent()]);
                $message = trans('message.error_description_500');
                if ($response->status() == 404) {
                    $message = trans('message.data_not_found');
                } elseif ($response->status() == 403) {
                    $message = trans('message.you_are_not_allowed_perform');
                } elseif ($response->status() == 503) {
                    $message = trans('message.error_description_503');
                }
                return response()->json([
                    'message' => $message,
                ], $response->status());
            }
        }
        return $response;
    }
}

ServiceException.php

<?php

namespace App\Exceptions;

use Exception;

class ServiceException extends Exception
{
    /**
     * Report the exception.
     *
     * @return void
     */
    public function report()
    {
        //
    }

    /**
     * Render the exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function render($request, $exception)
    {
        if (! $request->ajax()) {
            // view('error_handler', compact('exception'));
        }

        return response()->json([
            'code' => $exception->getCode(),
            'status' => 'error',
            'message' => $exception->getMessage(),
            'data' => 'sample data',
        ]);
    }
}

AnyController.php

public function anyFunction() {
    // throw new Exception('Unhandled Exception.'); // It will call the $this->reportable(function (Throwable $e) { block.
    // throw new ServiceException('Unhandled ServiceException.'); // It will call the $this->reportable(function (Throwable $e) { block.

    try {
        $this->service->aFunctionThatThrowException(); // this function will throw ServiceException.
    } catch (Exception $e) {
        Log::error('controller_fun_error', ['error' => $e->getMessage()]);
        report($e);
        return $this->failResponse();
    }
}

AnyService.php

public function aFunctionThatThrowException() {
    try {
        throw new ServiceException('Throwing ServiceException...)');
    } catch (ServiceException | Exception $e) {
        Log::error('service_fun_error', ['error' => $e->getMessage()]);
        throw $e;
    }
}

CodePudding user response:

I'm not intirely sure, but I don't think the error will be reported if you put a try catch around it yourself.

CodePudding user response:

Create a new helper functions file eg: app/Helpers/Common.php and create a service provider HelperServiceProvider in app/Providers/HelperServiceProvider.php use the code below :

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        require_once __DIR__ . '/../Helpers/Common.php';
    }
}

Now copy the below mentioned code to the app/Helpers/Common.php file :

if (! function_exists('throwResponse')) {
    function throwResponse($message='Exceptions',$data=[],$statusCode=500){
        if(request()->wantsJson()) {
            if ((gettype($message) !== 'string') && ($message instanceof \Exception)) {
                if($message->getMessage()){
                    $data      = (!empty($message->getTrace()))   ? $message->getTrace()   : [];
                    $message   = (!empty($message->getMessage())) ? $message->getMessage() : "Something went wrong";
                    $data      = $data?:[$message];
                    $statusCode = 500;
                }else{
                    throw new \Illuminate\Http\Exceptions\HttpResponseException($message->getResponse());
                }
            }
            $errStatus = (in_array($statusCode,[200,201])) ? false : true;

            $response = ['code'=>(int)$statusCode,
                         'error'=>$errStatus,
                         'message'=>$message];

            if(!empty($data)){
                $response['data'] = $data;
            }
            if($statusCode == 200 && $data == "empty"){
                $response['data'] = [];
            }           
            throw new \Illuminate\Http\Exceptions\HttpResponseException(response()->json($response,$statusCode));
        } else{
            if(is_object($message)){
                throw $message;
            }else{
                return $message;
            }
        }
    }
    
}

Now you can easily call the throwResponse function from anywhere in the project : for normal response

throwResponse('Message for the response',['user_id'=>1],200);

{
  "code":200,
  "error":false,
  "message":"Message for the response",
  "data":{
       "user_id":1
   }
}

or

throwResponse('unauthorized',null,401);

{
  "code":401,
  "error":true,
  "message":"unauthroized",
}

or for exceptions simply pass the exception $e

throwResponse($e);

{
  "code":500,
  "error":true,
  "message":"exception message",
  "data":{
       exception data
    }
}

The response will be

for example :

   public function exampleFunction(Request $request){
     try{
         
         if(empty($request->userId)){
            throwResponse('Validation error',null,401);
         }
          
         $userData = User::find($request->userId);
         if(!empty($userData)){
             throwResponse('User data',$userData,401);
         }else{
             throwResponse('Invalid user id',null,422);
         }                          
     }catch(\Exception $e){
         throwResponse($e);
    }
  }

Change responses according to your need

  • Related