10

I am trying to write a CRUD for an API. However, when the validation fail, instead of redirecting the user to the home page, I want to return json based response with the errors.

I am able to do that using the following code

public function store(Request $request)
{
    try {
        $validator = $this->getValidator($request);

        if ($validator->fails()) {
            return $this->errorResponse($validator->errors()->all());
        }

        $asset = Asset::create($request->all());

        return $this->successResponse(
            'Asset was successfully added!',
            $this->transform($asset)
        );
    } catch (Exception $exception) {
        return $this->errorResponse('Unexpected error occurred while trying to process your request!');
    }
}

/**
 * Gets a new validator instance with the defined rules.
 *
 * @param Illuminate\Http\Request $request
 *
 * @return Illuminate\Support\Facades\Validator
 */
protected function getValidator(Request $request)
{
    $rules = [
        'name' => 'required|string|min:1|max:255',
        'category_id' => 'required',
        'cost' => 'required|numeric|min:-9999999.999|max:9999999.999',
        'purchased_at' => 'nullable|string|min:0|max:255',
        'notes' => 'nullable|string|min:0|max:1000',
    ];

    return Validator::make($request->all(), $rules);
}

Now, I like to extract some of my code into a form-request to clean up my controller little more. I like to change my code to something like the code below.

public function store(AssetsFormRequest $request)
{
    try {
        if ($request->fails()) {
            return $this->errorResponse($request->errors()->all());
        }            
        $asset = Asset::create($request->all());

        return $this->successResponse(
            'Asset was successfully added!',
            $this->transform($asset)
        );
    } catch (Exception $exception) {
        return $this->errorResponse('Unexpected error occurred while trying to process your request!');
    }
}

As you can probably tell that $request->fails() and $request->errors()->all() is not going to work. How can I check if the request failed and then how can I get the errors out of the form-request?

For your reference, here is how my AssetsFormRequest class look like

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class AssetsFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string|min:1|max:255',
            'category_id' => 'required',
            'cost' => 'required|numeric|min:-9999999.999|max:9999999.999',
            'purchased_at' => 'nullable|string|min:0|max:255',
            'notes' => 'nullable|string|min:0|max:1000',
        ];
    }
}
2
  • Possible duplicate of Unit Test Laravel's FormRequest Commented Mar 28, 2019 at 2:43
  • Friends, please, make the unit-test properly, after all, it is not only rules you are testing here, the validationData and withValidator functions may be there too. here is my answer Commented Mar 28, 2019 at 2:44

3 Answers 3

18

In your AssetFormRequest class, you can override failedValidation method to the following-

public $validator = null;
protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator)
{
    $this->validator = $validator;
}

Then your controller method, do anything you want with your $validator object. May be something like the following-

if (isset($request->validator) && $request->validator->fails()) {
        return response()->json($request->validator->messages(), 400);
    }

You can see this link too for further details. Hope it helps :)

Sign up to request clarification or add additional context in comments.

Comments

4

Add this function to your Request:

public function withValidator($validator)
    {
        if ($validator->fails()) {
            Session::flash('error', 'Flash error!');
        } else {

        }

    }

2 Comments

I think the approach would be more of a Http error, rather, than only a flash message, no?
I am using this method after success request for manipulations. Nice trick!
1

It's been 2 years, but maybe this will help someone.

You can override the getValidatorInstance() method in your AssetFormRequest by adding(Tested in Laravel 6.0.4):

use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Contracts\Validation\Validator;

/**
 * Get the validator instance for the request.
 *
 * @return Validator
 * @throws BindingResolutionException
 */
public function getValidatorInstance()
{
    if ($this->validator) {
        return $this->validator;
    }

    $factory = $this->container->make(ValidationFactory::class);

    if (method_exists($this, 'validator')) {
        $validator = $this->container->call([$this, 'validator'], compact('factory'));
    } else {
        $validator = $this->createDefaultValidator($factory);
    }

    if (method_exists($this, 'withValidator')) {
        $this->withValidator($validator);
    }

    $this->setValidator($validator);

    return $this->validator;
}

After that, a validator becomes available to you in your controller:

public function store(AssetsFormRequest $request)
{
    $validator = $request->getValidatorInstance();

    try {
        if ($validator->fails()) {
            return $this->errorResponse($validator->errors());
        }            
        $asset = Asset::create($validator->validated());

        return $this->successResponse(
            'Asset was successfully added!',
            $this->transform($asset)
        );
    } catch (Exception $exception) {
        return $this->errorResponse('Unexpected error occurred while trying to process your request!');
    }
}

Comments

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.