try/catch PHP doesn't catch exceptions (laravel controller)

Viewed 6646

I am working with Laravel and try to use try/catch as I would do for java. Unfortunately, it doesn't work as expected... The exception isn't catched, and instead of returning an error message, it makes a 422 exception.

Here is my function:

public function changePassword(Request $request){

        try{
            if (!(Hash::check($request->get('currentpassword'), Auth::user()->password))) {
                return "Your current password does not matches with the password you provided. Please try again.";
            }

            if(strcmp($request->get('currentpassword'), $request->get('new-password')) == 0){
                return "New Password cannot be same as your current password. Please choose a different password.";
            }

            $validatedData = $request->validate([
                'currentpassword' => 'required',
                'newpassword' => 'required|string|min:6',
            ]);

            $user = Auth::user();
            $user->password = bcrypt($request->get('newpassword'));
            $user->save();

            return "Password changed successfully !";
        }
        catch(Exception $error){
            return $error->getMessage();
        }
   }

I call this method like this

Route::post('memberform/changePassword','MemberController@changePassword')->name('changePassword');

Here i would like to get my exception message, and display it. Instead, I get an error while using my request, and this exception isn't catched

422 Unprocessable Entity {"message":"The given data was invalid.","errors":{"newpassword":["The newpassword must be at least 6 characters."]}}

Thanks a lot

5 Answers

Your error handling code is correct. Your code for catching an Exception is the way to do it in PHP and it works the same way as in Java (I have coded both). In short there is nothing wrong with your code.

My guess is one of two things, and I'm not 100% certain of either of them:

1) You are testing on OSX and some XDebug settings can cause problem with error handling when things are massively nested (I personally experienced this in migrations). SO XDebug settings problem

2) Laravel has an interceptor that catches the error when it occurs and that handler has been injected to take precedence before your handler. SO form validation exception not catching

Hopefully this will nudge you in the right direction. I'm sorry this is a non-answer type answer.

Use getMessage() method.eg:-

$error->getMessage();

Use this in catch block. It will work for you. Enjoy!!

You can try this

public function changePassword(Request $request){

        try{
            if (!(Hash::check($request->get('currentpassword'), Auth::user()->password))) {
                return "Your current password does not matches with the password you provided. Please try again.";
            }

            if(strcmp($request->get('currentpassword'), $request->get('new-password')) == 0){
                return "New Password cannot be same as your current password. Please choose a different password.";
            }

            $validatedData = $request->validate([
                'currentpassword' => 'required',
                'newpassword' => 'required|string|min:6',
            ]);

            $user = Auth::user();
            $user->password = bcrypt($request->get('newpassword'));
            $user->save();

            return "Password changed successfully !";
        }
        catch(\Exception $error){
            return $error->getMessage();
        }
   }

Laravel validation fail doesn't throw exception !! So you can't catch.... If you want to catch , use custom validation like below and throw exception yourself

public function changePassword(Request $request)
{
    try
    {
       $data['currentpassword'] = $request->get('currentpassword');
       $data['newpassword'] = $request->get('newpassword');
        if (!(Hash::check($request->get('currentpassword'), Auth::user()->password))) {
            $message['currentpassword.required'] = "Your current password does not matches with the password you provided. Please try again.";
            $data['currentpassword'] = ""; // I used for required rule as a example , but I recommend to create custom rule for this
        }

        if(strcmp($request->get('currentpassword'), $request->get('new-password')) == 0){
            $message['newpassword.required'] = "New Password cannot be same as your current password. Please choose a different password.";
            $data['newpassword'] = "";// I used for required rule as a example , but I recommend to create custom rule for this
        }

        $rule = [
            'currentpassword' => 'required',
            'newpassword' => 'required|string|min:6',
        ];
        $validatedData = \Illuminate\Support\Facades\Validator::make($data, $rule, $message);
        if($validateData->fails()) {
           throw new \Exception($validateData->messages());
        }

        $user = Auth::user();
        $user->password = bcrypt($request->get('newpassword'));
        $user->save();

        return "Password changed successfully !";
    }
    catch(Exception $error)
    {
        return $error->getMessage();
    }
}

Where are you returning the $error, you can return $error in view() or use redirect() or response() etc...

example:

return back()->back()->withError($error->getMessage())->withInput();

another example:

return response()->json('error_message' => $error->getMessage());

I hope you get the idea.

In blade view you can show $error message like this:

@if (session('error'))
    <div class="alert alert-danger" style="margin-top: 0.2rem;">
        {{ session('error') }}
    </div>
@endif
Related