Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

alexleonard's avatar

L5: API Validation and the new FormRequest approach

Following on from the Laracast: https://laracasts.com/series/incremental-api-development/episodes/7

Does anyone have a nice approach which couples the new L5 FormRequest injection with the nice layout for triggering error responses via the base api controller?

At the moment I'm thinking I will need to abandon FormRequests and just use old style Validator::make on my API store methods if I want to make use of the base API responses.

Any thought appreciated!

0 likes
13 replies
coopers98's avatar

I ran into the same problem and went with the old style Validator in my API methods.

pmall's avatar

What do you mean by "the nice layout for triggering error responses via the base api controller?"

alexleonard's avatar

@pmall If you watch the incremental API development series, you'll see what I mean.

Basically all responses are extracted to a base ApiController which each ApiModelController extends, so you can do stuff like

if($validation->fails()){
 return $this->setStatusCode(422)
    ->respondWithError('Basic validation failed');
}

But if we intercept validation with FormRequest injection, then the responses aren't available as it's captured before we hit the controller method.

pmall's avatar

Hum ok I didn't remembered because FormRequest do returns a 422 response with json errors but it is in case of an ajax request :

// vendor/laravel/src/Illuminate/Foundation/Http/FormRequest.php
 public function response(array $errors)
 {
  if ($this->ajax())
  {
   return new JsonResponse($errors, 422);
  }

  return $this->redirector->to($this->getRedirectUrl())
                                        ->withInput($this->except($this->dontFlash))
                                        ->withErrors($errors);
 }

It may work with wantsJson() instead of ajax().

arabsight's avatar

you can override the response method and return your custom response. laravel now has a Request abstract class that extends FormRequest, you can put your overridden method there since the Request class is the base class of all your requests.

1 like
alexleonard's avatar

Thanks for the answers guys. Much appreciated. I'll have another go at it on Monday :)

pmall's avatar

Anyway I really think FormRequest should use wantsJson() instead ajax() by default.

socks's avatar

+1 to @arabsight. I moved the base controller functions to a trait and am overriding the response function.

    use ApiResponse;

    public function response(array $errors)
    {
        return $this->respondBadRequest(array_flatten($errors));
    }
4 likes
mhess's avatar

I took Socks approach and did one more thing. Since I am using the same controller method for both API and application form input I added one more thing.

In the application form I added a hidden Form input which can be anything. But then in the response I can check if that Input field actually exists in the request. If it does I know it is from a Form and can treat it normally. If that field isn't there then I know it is an API request and can handle that response appropriately.

I handle it in my response in my trait if it from API and if not just pass it along to be handled normally.

pmall's avatar

@mhess your API is expecting json data right ? Why not just use $request->wantsJson() ?

mhess's avatar

Ooooo. I like. Thanks. I hadn't thought of that detail to narrow down the requesting origin. I wasn't crazy about the hidden field but nothing else popped in my head.

omniware's avatar

With Laravel 5.3 I did following in app/Http/Requests/PostRequest.php

/*
 * Validator instance updated on failedValidation
 *
 * @var \Illuminate\Contracts\Validation\Validator
 */
public $validator = null;

/**
 * Overrid Handle a failed validation attempt.
 *
 * @param  \Illuminate\Contracts\Validation\Validator  $validator
 * @return void
 *
 * @throws \Illuminate\Validation\ValidationException
 */
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
//      throw new \Illuminate\Validation\ValidationException($validator, $this->response(
//          $this->formatErrors($validator)
//      ));
    $this->validator = $validator;
}

In my Controller function, I captured the $validation object

public function create(PostRequest $request)
{
    if (isset($request->validator) && $request->validator->fails()) {
        throw  new \Exception('GEN-INVALID-PARAMS - ' . $request->validator->messages()->first(), 999);
    }
...

Hope this helps

Please or to participate in this conversation.