Laravel Validation Rules If Value Exists in Another Field Array
Asked Answered
R

5

23

I am working in Laravel 5.4 and I have a slightly specific validation rules need but I think this should be easily doable without having to extend the class. Just not sure how to make this work..

What I would like to do is to make the 'music_instrument' form field mandatory if program array contains 'Music'.

I found this thread How to set require if value is chosen in another multiple choice field in validation of laravel? but it is not a solution (because it never got resolved in the first place) and the reason it doesn't work is because the submitted array indexes aren't constant (not selected check boxes aren't considered in indexing the submission result...)

My case looks like this:

<form action="" method="post">
    <fieldset>

        <input name="program[]" value="Anthropology" type="checkbox">Anthropology
        <input name="program[]" value="Biology"      type="checkbox">Biology
        <input name="program[]" value="Chemistry"    type="checkbox">Chemistry
        <input name="program[]" value="Music"        type="checkbox">Music
        <input name="program[]" value="Philosophy"   type="checkbox">Philosophy
        <input name="program[]" value="Zombies"      type="checkbox">Zombies

        <input name="music_instrument" type="text" value"">

        <button type="submit">Submit</button>

    </fieldset>
</form>

If I select some of the options from the list of check boxes I can potentially have this result in my $request values

[program] => Array
    (
        [0] => Anthropology
        [1] => Biology
        [2] => Music
        [3] => Philosophy
    )

[music_instrument] => 'Guitar'

Looking at validation rules here: https://laravel.com/docs/5.4/validation#available-validation-rules I think something like his should work but i am literally getting nothing:

  $validator = Validator::make($request->all(),[
        'program'           => 'required',
        'music_instrument'  => 'required_if:program,in:Music'
  ]);

I was hoping this would work too but no luck:

'music_instrument'  => 'required_if:program,in_array:Music',

Thoughts? Suggestions?

Thank you!

Rodent answered 31/3, 2017 at 18:42 Comment(1)
From docs: required_array_keys:foo,bar,...Iraqi
I
43

Haven't tried that, but in general array fields you usually write like this: program.*, so maybe something like this will work:

  $validator = Validator::make($request->all(),[
        'program'           => 'required',
        'music_instrument'  => 'required_if:program.*,in:Music'
  ]);

If it won't work, obviously you can do it also in the other way for example like this:

$rules = ['program' => 'required'];

if (in_array('Music', $request->input('program', []))) {
    $rules['music_instrument'] = 'required';
}

$validator = Validator::make($request->all(), $rules);
Incorruption answered 31/3, 2017 at 18:54 Comment(3)
I tried the asterisk out but it is ignored. I was able to get it to work with required_if:program.34,Music,,, but for that I have to index my list and keep track of 34 which is obviously not a good idea... I will try this aproach too. Thanks. I was hoping laravel's in_array would work but no luckRodent
I will accept your second solution being that that is the way I went :) Thanks! For the sake of argument, if someone else comes up with an out of the box working configuration I would still like to see it... heheRodent
The second way is only way that I can use in this situation.Lylalyle
O
14

I know this post is older but if someone came across this issue again.

$validator = Validator::make($request->all(),[
    'program' => 'required',
    'music_instrument'  => 'required_if:program,Music,other values'
]);
Omari answered 11/1, 2021 at 16:53 Comment(2)
Thanks @Noman Sheikh, you save my timeShellacking
From docs: required_array_keys:foo,bar,...Iraqi
I
4

You could create a new custom rule called required_if_array_contains like this...

In app/Providers/CustomValidatorProvider.php add a new private function:

/**
 * A version of required_if that works for groups of checkboxes and multi-selects
 */
private function required_if_array_contains(): void
{
    $this->app['validator']->extend('required_if_array_contains',
        function ($attribute, $value, $parameters, Validator $validator){

            // The first item in the array of parameters is the field that we take the value from
            $valueField = array_shift($parameters);

            $valueFieldValues = Input::get($valueField);

            if (is_null($valueFieldValues)) {
                return true;
            }

            foreach ($parameters as $parameter) {
                if (in_array($parameter, $valueFieldValues) && strlen(trim($value)) == 0) {
                    // As soon as we find one of the parameters has been selected, we reject if field is empty

                    $validator->addReplacer('required_if_array_contains', function($message) use ($parameter) {
                        return str_replace(':value', $parameter, $message);
                    });

                    return false;
                }
            }

            // If we've managed to get this far, none of the parameters were selected so it must be valid
            return true;
        });
}

And don't forget to check there is a use statement at the top of CustomValidatorProvider.php for our use of Validator as an argument in our new method:

...

use Illuminate\Validation\Validator;

Then in the boot() method of CustomValidatorProvider.php call your new private method:

public function boot()
{
    ...

    $this->required_if_array_contains();
}

Then teach Laravel to write the validation message in a human-friendly way by adding a new item to the array in resources/lang/en/validation.php:

return [
    ...

    'required_if_array_contains' => ':attribute must be provided when &quot;:value&quot; is selected.',
]

Now you can write validation rules like this:

public function rules()
{
    return [
        "animals": "required",
        "animals-other": "required_if_array_contains:animals,other-mamal,other-reptile",
    ];
}

In the above example, animals is a group of checkboxes and animals-other is a text input that is only required if the other-mamal or other-reptile value has been checked.

This would also work for a select input with multiple selection enabled or any input that results in an array of values in one of the inputs in the request.

Impersonality answered 8/4, 2019 at 15:26 Comment(1)
From docs: required_array_keys:foo,bar,...Iraqi
E
3

The approach I took for a similar problem was to make a private function inside my Controller class and use a ternary expression to add the required field if it came back true.

I have roughly 20 fields that have a checkbox to enable the input fields in this case, so it may be overkill in comparison, but as your needs grow, it could prove helpful.

/**
 * Check if the parameterized value is in the submitted list of programs
 *  
 * @param Request $request
 * @param string $value
 */
private function _checkProgram(Request $request, string $value)
{
    if ($request->has('program')) {
        return in_array($value, $request->input('program'));
    }

    return false;
}

Using this function, you can apply the same logic if you have other fields for your other programs as well.

Then in the store function:

public function store(Request $request)
{
    $this->validate(request(), [
    // ... your other validation here
    'music_instrument'  => ''.($this->_checkProgram($request, 'music') ? 'required' : '').'',
    // or if you have some other validation like max value, just remember to add the |-delimiter:
    'music_instrument'  => 'max:64'.($this->_checkProgram($request, 'music') ? '|required' : '').'',
    ]);

    // rest of your store function
}
Eosinophil answered 23/10, 2017 at 14:34 Comment(0)
L
3

Here my piece of code to solve that kind of trouble usind Laravel 6 Validation Rules

I tried to use the code above

public function rules() 
{
    return [
      "some_array_field.*" => ["required", "integer", "in:1,2,4,5"],
      "another_field" => ["nullable", "required_if:operacao.*,in:1"],
    ];
}

I need that when some_array_field has 1 in your value, another_field must be validated, otherwhise, can be null. With the code above, doesn't work, even with required_if:operacao.*,1

If I change the rule for another_field to required_if:operacao.0,1 WORKS but only if the value to find is in index 0, when the order changes, validation fails.

So, I decided to use a custom closure function

Here's the final code for the example that works fine form me.

public function rules() 
{
    return [
      "some_array_field.*" => ["required", "integer", "in:1,2,4,5"],
      "another_field" => [
          "nullable",
          Rule::requiredIf (
              function () {
                  return in_array(1, (array)$this->request->get("some_array_field"));
              }
          ),
        ]
    ];
}

I hope that solve your trouble too!

Lodi answered 14/10, 2020 at 3:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.