laravel/lumen-framework

Stop Validation for nested fields if Parent field has validation error for implicit rule

akkivarma opened this issue · 3 comments

  • Lumen Version: 8.2.0 (Laravel Components ^8.0)
  • PHP Version: 7.3.1

Description:

When running a custom implicit rule on array field, validation for nested fields in array should not run if there is validation error in array field for implicit rule.

Steps To Reproduce:

Example API to store blogs in bulk:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BlogController extends Controller
{
    public function store(Request $request)
    {
        $rules = [
            'blogs' => 'required|strict_array|max:3',
            'blogs.*.title' => 'required|string|max:100|distinct',
            'blogs.*.body' => 'required|string|max:1000',
        ];

        $this->validate($request, $rules);

        return response()->json(['status' => 'success']);
    }

}

Custom Strict Array Validator:

<?php

namespace App\Providers;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Validator::extendImplicit('strict_array', function ($attribute, $value, $parameters, \Illuminate\Validation\Validator $validator) {
            if (!$validator->validatePresent($attribute, $value)) return true;

            if ($validator->hasRule($attribute, ['Nullable']) && is_null($value)) return true;

            return $validator->validateArray($attribute, $value) && !Arr::isAssoc($value);
        });
    }
}

Sample JSON Input:

{
    "blogs": {
        "0": 1,
        "g": 4
    }
}

API Response:

{
    "blogs": [
        "The blogs must be strictly an array."
    ],
    "blogs.0.title": [
        "The blogs.0.title field is required."
    ],
    "blogs.g.title": [
        "The blogs.g.title field is required."
    ],
    "blogs.0.body": [
        "The blogs.0.body field is required."
    ],
    "blogs.g.body": [
        "The blogs.g.body field is required."
    ]
}

As we can see from this response that validations ran for blogs.0 & blogs.g even though the blogs field had implicit validation error.

Hi there,

Thanks for reporting but it looks like this is a question which can be asked on a support channel. Please only use this issue tracker for reporting bugs with the library itself. If you have a question on how to use functionality provided by this repo you can try one of the following channels:

However, this issue will not be locked and everyone is still free to discuss solutions to your problem!

Thanks.

Shouldn't this be handled by lumen validator itself ?

In lumen's Illuminate\Validation\Validator class, in the function shouldStopValidating there is a check which checks for implicit rule failure & stops further validating a field.

I think that same check should be there for parent field as well before validating nested child fields.

    /**
     * Check if we should stop further validations on a given attribute.
     *
     * @param  string  $attribute
     * @return bool
     */
    protected function shouldStopValidating($attribute)
    {
        // other code

        // In case the attribute has any rule that indicates that the field is required
        // and that rule already failed then we should stop validation at this point
        // as now there is no point in calling other rules with this field empty.
        return $this->hasRule($attribute, $this->implicitRules) &&
               isset($this->failedRules[$cleanedAttribute]) &&
               array_intersect(array_keys($this->failedRules[$cleanedAttribute]), $this->implicitRules);
    }