1

Usually Laravel's form request returns a generic 400 code on failing validation like this:

{
  "message": "The given data is invalid",
  "errors": {
    "person_id": [
      "A person with ID c6b853ec-b53e-4c35-b633-3b1c2f27869c does not exist"
    ]
  }
}

I'd like to return a 404 if a request does not pass my custom rule.

My custom rule checks if a record exists in the DB:

class ValidatePersonExists implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
    public function passes($attribute, $value)
    {
        return Person::where('id', $value)->exists();
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return "A person with ID :input does not exist";
    }
}

If I throw a ModelNotFoundException on failure of the exists() check, where can I catch it to respond with a friendly 404 response?

Here's my form request where I am using the rule:

public function rules()
{
    return [
        'person_id' => ['bail', 'required', 'uuid', new ValidatePersonExists],
    ];
}
3
  • How you handling it in controller? Commented Sep 15, 2020 at 10:28
  • It's not reaching the controller. Laravel's form request performs validation, and it returns there. Commented Sep 15, 2020 at 10:29
  • Then try by validating your data in controller only using Validator and if validator fails you can send 404 status code. Commented Sep 16, 2020 at 14:55

2 Answers 2

0

you can modify app/Exceptions/Handler.php, here is how i used it

use Illuminate\Database\Eloquent\ModelNotFoundException;

public function render($request, Exception $e)
{
    $status = method_exists($e, 'getStatusCode') ? $e->getStatusCode() : 500;
    //here im using translation, you can set your own message
    $response = [
        'errors' => trans("response.$status.response"),
        'message' => trans("response.$status.message")
    ];

    if (config('app.debug')) {
        $response['exception'] = get_class($e);
        $response['message'] = $e->getMessage();
        $response['trace'] = $e->getTrace();
    }
    //ModelNotFoundException statusCode is 0 so i need to pass it manually 
    if($e instanceof ModelNotFoundException){
        $response['errors'] = new \Illuminate\Support\ViewErrorBag;
        $response['response'] = 404; // im using translation here
        return response()->view("errors.index", $response, 404);
    }
    
    return parent::render($request, $e);
}

in my blade a simple translation message:

<p>{{trans("response.{$response}.response")}}</p>
<p>{{trans("response.{$response}.message")}}</p>
Sign up to request clarification or add additional context in comments.

Comments

0

I've found a solution, but I'm not 100% happy with it.

Basically I've created a class (ApiRequest.php) to extend Illuminate's FormRequest class, and in this ApiRequest class I'm intercepting the IlluminateValidationException and doing some logic on the failed validation, checking if the Request failed on my exists() rule. If it does, I'm changing the status code to 404:

Here's my class:

abstract class ApiRequest extends IlluminateFormRequest
{
    /**
     * Handle a failed validation attempt.
     *
     * @param \Illuminate\Contracts\Validation\Validator $validator
     * @return void
     *
     * @throws \GetCandy\Api\Exceptions\ValidationException
     */
    protected function failedValidation(Validator $validator)
    {
        $failedRules = $validator->failed();
        $statusCode = $this->getStatusCode($failedRules);

        $response = new JsonResponse([
            'message' => 'The given data is invalid',
            'errors' => $validator->errors(),
        ], $statusCode);

        throw new IlluminateValidationException($validator, $response);
    }

    private function getStatusCode($failedRules)
    {
        $statusCode = 400;

        foreach ($failedRules as $rule) {
            if (Arr::has($rule, "App\Http\Requests\Rules\ValidatePersonExists")) {
                $statusCode = 404;
            }
        }

        return $statusCode;
    }
}

Hope this helps someone, if anyone has a better solution feel free to post an answer.

1 Comment

im using Illuminate\Http\Exceptions\HttpResponseException for an api error validation and Illuminate\Validation\ValidationException for formData in failedValidation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.