5

Suppose we have the following form data:

{age: 15, father: "John Doe"}

We want to have some complex logic behind validating the father field based on other data for the same item (for this example, we want to validate that father has at least 5 characters when age < 18).

This could be done like that:

Standard validation rules: ['age': 'required|integer']

$validator->sometimes('father', 'required|min:5', function($data) {
    return $data['age'] < 18;
});

Now, we want to have the same kind of thing with a list of items. So now, we have the following form data:

[
  {age: 25, },
  {age: 15, father: "John Doe"},
  {age: 40, },
]

Common validation rule now looks like that:

['items.*.age': 'required|integer']

My issue is now to easily express the sometimes rule for each item's father field which will depend on the item's age field.

$validator->sometimes('items.*.father', 'required|min:5', function($data) {
    // Does not work anymore: return $data['age'] < 18;
    // Any way to know which item we are dealing with here?
});

One way I could think of is to loop over the items in a validator after callback. But that does not seem very elegant :(

3 Answers 3

4
+50

Couldn't get sometimes() to work in a way you need. sometimes() doesn't "loop" over array items, it gets called a single time.

I came up with an alternative approach, which isn't perfect, but maybe you'll find it useful.

/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    Validator::extend('father_required_if_child', function ($attribute, $value, $parameters, $validator) {

        $childValidator = Validator::make($value, [
            'age' => 'required|integer'
        ]);

        $childValidator->sometimes('father', 'required|min:5', function($data) {
            return is_numeric($data['age']) && $data['age'] < 18;
        });

        if (!$childValidator->passes()) {
            return false;
        }

        return true;

        // Issue: since we are returning a single boolean for three/four validation rules, the error message might
        // be too generic.

        // We could also ditch $childValidator and use basic PHP logic instead.
    });

    return [
        'items.*' => 'father_required_if_child'
    ];
}

Curious to learn how this could be improved.

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

Comments

1

The shortest approach I could get to work is this:

"items" => "required|array",
"items.*.age" => "required|integer",
"items.*.father" => "required_if:object.*.age,".implode(",",range(0,18))."|min:5"

Anytime a father is needed for that person, it should be having at least 5 characters. A father is needed when the person's age is below 18.

required_if works with multiple equations separated with commas. Therefore I wrote 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 as implode(',',range(0,18)) to get that part.

The test setup I've used:

Controller (used HomeController)

function posttest(Request $request) {
    $validator = Validator::make($request->all(), [
        "object" => "required|array",
        "object.*.age" => "required|integer",
        "object.*.father" => "required_if:object.*.age,".implode(",",range(0,18))."|min:5"
    ]);

    if($validator->fails()){
        dd($validator->errors());
    }
}

View (test.blade.php)

@extends('layouts.app')
@section('content')
<form action="{{URL::to("/posttest")}}" method="POST">
@csrf
<input type="number" name="object[0][age]" value="12">
<input type="text" name="object[0][father]" value="John">
<input type="number" name="object[1][age]" value="15">
<input type="text" name="object[1][father]" value="John Doe">
<input type="number" name="object[2][age]" value="17">
<input type="submit" value="gönder">
</form>
@endsection

Routes

Route::view("/test", "test");
Route::post('/posttest', "HomeController@posttest");

1 Comment

Clever workaround, but the condition may be more complicated than the one in our example. +1 though :)
0

I make it happen using the value of the second parameter of the callback function of the sometimes method of the Validator class. Here is how I have done this using laravel 8.

$validator->sometimes('father', 'required|min:5', function($data,$instance) {
    return $instance['age'] < 18;
});

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.