Search code examples
laravellaravel-5laravel-validationlaravel-5.5

Failed validation returns default error message even though custom messages supplied


I'm not getting the response I expect.

This is the controller code for a Location web-service request:

<?php
namespace App\Http\Controllers;

use App\Location;
use Illuminate\Http\Request;


class LocationController extends Controller
{


    /**
     * Action method to add a location with the supplied Data
     * 
     * @param \Illuminate\Http\Request $p_oRequest Request
     * 
     * @return JSON
     */
    public function add(Request $p_oRequest)
    {

        try {

            $p_oRequest->validate(
                array(
                    'name' => 'required|alpha_num',
                    'user_id' => 'required|integer',
                ),
                array(
                    'name.required' => 'Name is required',
                    'name.string' => 'Name must be alphanumeric',
                    'user_id.required' => 'Curator User Id is required',
                    'user_id.required' => 'Curator User Id must be an integer',
                )
            );

        } catch (\Exception $ex) {

            $arrResponse = array(
                'result' => 0,
                'reason' => $ex->getMessage(),
                'data' => array(),
                'statusCode' => 404
            );

        } finally {

            return response()->json($arrResponse);

        }

    }

}

The request is http://mydomain/index.php/api/v1/location/add?name=@!^

The response reason I expect is: { "result": 0, "reason": "Name must be alphanumeric", "data": [], "statusCode": 404 }

The actual response I get instead is: { "result": 0, "reason": "The given data was invalid.", "data": [], "statusCode": 404 }

Please help. This is bugging me.


Solution

  • I've finally discovered why this isn't working. It's not an issue of errors in the implementing code or Laravel, but one of either: (i). writing good PHP code to handle the self-evident result, which clearly I didn't do; (ii). insufficient documentation within Laravel on how to actually use the validation error response. Take your pick.

    Laravel's validation throws a Illuminate\Validation\ValidationError. Believe it or not, this actually defaults the error message to "The given data was invalid.", so when you catch an \Exception and retrieve its $e->getMessage(), this default error-message is what you (correctly) get.

    What you need to do is capture the \Illuminate\Validation\ValidationError - which I should've done originally, duh! - and then use its methods to help you distill the error messages from it.

    Here's the solution I've come up with:

    <?php
    namespace App\Http\Controllers;
    
    use App\Location;
    use Illuminate\Http\Request;
    
    
    class LocationController extends Controller
    {
    
        /**
         * Action method to add a location with the supplied Data
         * 
         * @param \Illuminate\Http\Request $p_oRequest Request
         * 
         * @return JSON
         */
        public function add(Request $p_oRequest)
        {
            try {
            
                $arrValid = array(
                    'name' => 'required|alpha_num',
                    'user_id' => 'required|integer',
                );
                $p_oRequest->validate(
                    $arrValid,
                    array(
                        'name.required' => 'Name is missing',
                        'name.alpha_num' => 'Name must be alphanumeric',
                        'user_id.required' => 'User Id is missing',
                        'user_id.integer' => 'User Id must be an integer',
                    )
                );
                
            } catch (\Illuminate\Validation\ValidationException $e ) {
            
                /**
                 * Validation failed
                 * Tell the end-user why
                 */
                $arrError = $e->errors(); // Useful method - thank you Laravel
                /**
                 * Compile a string of error-messages
                 */
                foreach ($arrValid as $key=>$value ) {
                    $arrImplode[] = implode( ', ', $arrError[$key] );
                }
                $message = implode(', ', $arrImplode);
                /**
                 * Populate the respose array for the JSON
                 */
                $arrResponse = array(
                    'result' => 0,
                    'reason' => $message,
                    'data' => array(),
                    'statusCode' => $e->status,
                );
    
            } catch (\Exception $ex) {
    
                $arrResponse = array(
                    'result' => 0,
                    'reason' => $ex->getMessage(),
                    'data' => array(),
                    'statusCode' => 404
                );
    
            } finally {
    
                return response()->json($arrResponse);
    
            }
            
        }
        
    }
    

    So, indeed, Laravel was supplying the correct response, and did what it said on the side of the tin, but I wasn't applying it correctly. Regardless, as a help to future me and other lost PHP-mariners at Laravel-sea, I provide the solution.

    In addition, thanks to Marcin for pointing out my buggy coding, which would've caused a problem even if I had implemented the above solution.