4

I have an API that sends an array of staff, some are existing objects that need to be updated and some are new objects that need to be created, they all need to be validated and as part of that is testing for a unique email. I am using a FormRequest:

  $rules = [
        'staff.*.name' => 'required|max:128',
        'staff.*.email' => 'required|email|unique:users',
        'staff.*.description' => 'max:512',            
    ];

So the problem is, as I am sure you can see, the email address fails unique validation on update. This is because the mechanism for ignoring the email if the ID is the same as the item being validated is causing me an issue.

I can not see a way of getting the ID of the object currently being validated so I can access its ID. So I can't add the part:

'staff.*.email' => 'required|email|unique:users,email,id,' . $currentStaff->id

I can't see much about this specific issue so I am assuming I am barking up the wrong tree doing it this way or missing something insanely obvious.

The payload below:

{
"staff": [
    {
        "name":"Libbie Turcotte",
        "email":"[email protected]",
        "updated_at":"2019-12-05 19:28:59",
        "created_at":"2019-12-05 19:28:59",
        "id":53
    },
    {
        "name":"Person Dave",
        "email":"[email protected]",
    },
    {
        "name":"Staff Name",
        "email":"[email protected]",

    }
  ]
}
5
  • 2
    This is doable, but requires some custom logic, specifically including ID somewhere in the staff[] array of input, either as staff[id] or staff[][id]. And the rules will need to be constructed based on the input, so you can loop it and reference: foreach($request->input('staff') AS $staff){ ... // use $staff['id'] }, or foreach($request->input('staff') AS $id => $staff){ ... // use $id }. If you can show your form/ajax request that is sending staff input, that would help. Commented Dec 5, 2019 at 15:15
  • I see what you are saying. The staff json is fairly simple, I'll update my question to include it. Commented Dec 5, 2019 at 19:26
  • 2
    Thanks for providing that! The answer below is actually demonstrating exactly what I was explaining; constructing the rules based on the input, and you can see that 1 and 2 have a different unique rule than 3, due to the inclusion/omission of id. Commented Dec 5, 2019 at 19:38
  • Yeah, just implementing that solution now. Makes sense. Thanks for your help in this. Commented Dec 5, 2019 at 19:56
  • 2
    phpunit tests/Feature/API/StaffTest.php --filter testAddAndUpdateStaff PHPUnit 8.4.3 by Sebastian Bergmann and contributors. . 1 / 1 (100%) Commented Dec 5, 2019 at 20:15

2 Answers 2

4

You can add the rules foreach request staff element looping through the array and merging the corresponding rule:

$rules = [  // this ones are ok for all
    'staff.*.name' => 'required|max:128',
    'staff.*.description' => 'max:512',
];
// here loop through the staff array to add the ignore
foreach($request->staff as $key => $staff) {
    if ( array_key_exists('id', $staff) && $staff['id'] ) { // if have an id, means an update, so add the id to ignore
        $rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users,id,'.$staff['id']]);
    } else {  // just check if the email it's not unique
        $rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users']);
    }
}

So, for this request

staff[1][id]=111
staff[1][email][email protected]
staff[2][id]=222
staff[2][email][email protected]
staff[3][email]=fff@ffff

You will have this rules:

[
    "staff.*.name" => "required|max:128",
    "staff.*.description" => "max:512",
    "staff.1.email": "required|email|unique:users,id,111",
    "staff.2.email": "required|email|unique:users,id,222",
    "staff.3.email": "required|email|unique:users"
]
Sign up to request clarification or add additional context in comments.

2 Comments

thanks, worked a charm! Minor edit to the rule: $rules = array_merge($rules, ['staff.'.$key.'.email' => 'required|email|unique:users,id,' . $staff['id']]);
Oh! Thanks for the feedback, I had missed that. Answer updated ;-)
-1

You can use just 'distinct' as like:

'staff.*.email' => 'distinct',

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.