4

I have a Vue form that let users add work experience for there profile. Users can add extra experience by clicking on a button. Clicking on that will add an new item with new input fields. I can't add the whole script because it's quit big. But here is an example to give you an idea:

<div class="item">
    <div class="row">
        <div class="form-group col-md-6">
            <label class="form-label">Title</label>
            <input type="text" name="experiences[0][title]" class="form-control">
        </div>
        <div class="form-group col-md-6">
            <label class="form-label">Institution</label>
            <input type="text" name="experiences[0][institution]" class="form-control">
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <textarea name="experiences[0][comments]" class="form-control"></textarea>
        </div>
    </div>
</div>
<div class="item">
    <div class="row">
        <div class="form-group col-md-6">
            <label class="form-label">Title </label>
            <input type="text" name="experiences[1][title]" class="form-control">
        </div>
        <div class="form-group col-md-6">
            <label class="form-label">institution </label>
            <input type="text" name="experiences[1][institution]" class="form-control">
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <textarea name="experiences[1][comments]" class="form-control"></textarea>
        </div>
    </div>
</div>

After each element there is a button to add a new row. This works fine but I have some validation issues. I only want to validate the fields if one of the fields has a value. For example: If experiences[0][institution] has a value, experiences[0][title] and experiences[0][comments] are required. This has to work in every order. If title has a value, the other fields are required. I can't really find out a way how to validate this. This is my validation rule:

$this->validate(request(), [
        'experiences.*.title'       => 'required_if:experiences.*.institution,null',
        'experiences.*.institution' => 'required_if:experiences.*.title,null',
]);

Problem here is that it simply doesn't validate. I can't figure out how to make a rule that says, if field X has a value, Y and Z are required.

Hope anyone here can help me finding a solution! :)

Ruub
  • 619
  • 1
  • 13
  • 41

3 Answers3

3

Like Azeame said, make a custom validation rule and check if all values are filled or all are empty. Something in de lines of:

public function passes($attribute, $value)
{
    $required = ['title','institution','comments'];

    $experience = collect($value)->reject(function ($item, $key) {
        return empty($item);
    });

    if (count($experience) == 0) {
        return true;
    }

    foreach ($required as $field) {
        if ( !$experience->has($field)) {
            return false;
        }
    }

    return true;
}

Maybe there is a beter way, but this should work.

spaantje
  • 58
  • 6
1

required_if doesn't work with null as it will treat it as a string ("null"), it will work with boolean values though.

Instead you can use the required_without rule:

$this->validate(request(), [
    "experiences.*.title"       => "required_without:experiences.*.institution",
    "experiences.*.institution" => "required_without:experiences.*.title",
]);

Example

$experiences = [
    [
        "title"       => "My title",
        "institution" => "",
        "comments"    => "<p>My first description</p>", //passes
    ],
    [
        "title"       => "",
        "institution" => "My title",
        "comments"    => "<p>My second description</p>", //passes
    ],
    [
        "title"       => "My title",
        "institution" => "My title",
        "comments"    => "<p>My third description</p>", //passes
    ],
    [
        "title"       => "",
        "institution" => null,
        "comments"    => "<p>My forth description</p>", //fails
    ],

];

$rules = [
    "experiences.*.title"       => "required_without:experiences.*.institution",
    "experiences.*.institution" => "required_without:experiences.*.title",
];

$validator = Validator::make(compact('experiences'), $rules);

dd($validator->fails());
Rwd
  • 34,180
  • 6
  • 64
  • 78
  • Somehow it skips the first part of the validation. If I leave `title` empty and give `institution` a value it doesn't validate. But the second part after the `merge`, works great :-) – Ruub Jan 12 '19 at 11:18
  • @Ruub can you show the output of `dd(request('experiences'))` for your above example? – Rwd Jan 12 '19 at 11:26
  • `[ "title" => null, "institution" => "My Company", "comments" => "

    My first description

    ", ], [ "title" => "My title", "institution" => null, "comments" => "

    My second description

    ", ] ];`
    – Ruub Jan 12 '19 at 11:33
  • Wait, the exact output is: `array:2 [ 0 => array:3 [ "title" => null "institution" => "My Company" "comments" => "

    My first description

    " ] 1 => array:3 [ "title" => "My title" "institution" => null "comments" => "

    My second description

    " ] ]`
    – Ruub Jan 12 '19 at 11:35
  • @Ruub Scratch all of that, it's an issue with the rule being used. I'll update my answer now. – Rwd Jan 12 '19 at 11:51
  • Hmm. Now it doesn't validate if one of the fields has an value. But it does if there are both empty. But that's not really what we are looking for :) – Ruub Jan 12 '19 at 11:58
  • @Ruub That's very strange as I'm getting the exact opposite. Just so I'm not missing something are you saying that if one of the fields has a value the validation fails and throws an exception but it doesn't if they're both empty? – Rwd Jan 12 '19 at 12:05
1

Write a custom rule with php artisan make:rule and in the passes() function write a check to ensure that all of the array keys are present and also that at least 2 of the array values are not null. I'm thinking something like this:

function passes($attribute, $value){
    if(array_keys($value) !== ['title','institution','comments']){
        return false;
    }
    if(empty($value['title']) && empty($value['institution'])){
        return false;
    }

    return true;
}

and in your $this->validate pass the rule as ['experiences.*' =>['array', new CustomRule()] instead of required_if...

I haven't checked this so feel free to edit if it's broken.

Azeame
  • 2,322
  • 2
  • 14
  • 30
  • I also had the idea to make a custom rule, but was really hoping there would be a more "easy" way of doing this :) – Ruub Jan 12 '19 at 11:20
  • It's actually really easy, give it a try, you'll be surprised, if I wasn't in my phone with no WiFi right now I'd just go ahead write it for you now – Azeame Jan 12 '19 at 11:24
  • @Ruub I updated the answer, see if this will work for you, I believe it should work well – Azeame Jan 12 '19 at 11:56
  • I will give it a try :) – Ruub Jan 12 '19 at 11:58
  • Hmm. It returns true because all three fields are present. But it doesn't check if they all have a value. – Ruub Jan 12 '19 at 12:09
  • How about combining my solution with Ross's, will that cover all the bases? – Azeame Jan 12 '19 at 12:12
  • I think so yes, I will come back with an update. Thank you guys! – Ruub Jan 12 '19 at 12:23
  • 1
    I edited it again, though I'm not there's actually a benefit in the first if statement, but it's 4:30am and I haven't slept yet, so I left it in for now, you could also replace the empty calls with a comparison to null, need sleep – Azeame Jan 12 '19 at 12:33