1
'email' => 'required|unique:users,email,'.$user->id

We all know that while we update an instance in database we should ignore its id when we are using unique validation rule, like the above example.
Now in my application I have an Agency model and an Employee model and each Agency has many Employee.
In my application I have a form to create and update Agencies and Employees.
Data sent by this form :

$request->validate([
    'agency.name' => 'required|string',
    'agency.phone' => 'required|string|unique:agencies,phone,'.$agency->id,
    .
    .
    .
    'employees.*.first_name' => 'required|string',
    'employees.*.last_name' => 'required|string',
    'employees.*.email' => 'required|unique:employees,email'
]);

Now, validating agency.phone is quite easy, because we only have 1 agency and we can easily pass $agency->id to ignore unique validation rule for this instance.

'agency.phone' => 'required|string|unique:agencies,phone,'.$agency->id

Now the problem that I'm facing is that I have multiple employees in my incoming data and I cannot use an id to ignore

'employees.*.email' => 'required|unique:employees,email' // cannot add $employee->id here in order to ignore while updating

I do have id of each employee in my incoming data, so how can I use it to ignore laravel validation rule for array incoming data like in this example?

HELP
The output of dd($request->all()) is :

array:2 [
  "agency" => array:7 [
    "id" => 5
    "name" => "xxxxxxx"
    "manager_name" => null
    "manager_phone" => null
    "phone" => null
    "created_at" => "2020-10-23T00:53:13.000000Z"
    "updated_at" => "2020-10-23T00:53:13.000000Z"
  ]
  "employees" => array:1 [
    0 => array:6 [
      "id" => 1
      "first_name" => "John"
      "last_name" => "Doe"
      "email" => "[email protected]"
      "created_at" => "2020-10-23T00:53:13.000000Z"
      "updated_at" => "2020-10-23T00:53:13.000000Z"
    ]
  ]
]
5
  • You can use custom validation rule, do you have all employee id's as an array? Commented Oct 23, 2020 at 5:08
  • I updated the question you can see incoming data from my form @sta Commented Oct 23, 2020 at 5:10
  • Try this 'employees.*.email' =>['required', Rule::unique( 'employees')->ignore( 'employees.*.id') ], Commented Oct 23, 2020 at 5:46
  • This may also work 'employees.*.email' => 'required|unique:employees,email,' . 'employees.*.id', Commented Oct 23, 2020 at 5:59
  • @sta you should never pass user data to the ignore, this will make it vulnerable to an SQL injection attack. Commented Oct 23, 2020 at 6:56

2 Answers 2

1

Just validate twice! :)

You could first validate all the things that don't require you to know the Employee IDs. Like so:

<?php

$request->validate([
    'agency.name' => 'required|string',
    'agency.phone' => 'required|string|unique:agencies,phone,'.$agency->id,
    // ...
    'employees.*.first_name' => 'required|string',
    'employees.*.last_name' => 'required|string',
    'employees.*.email' => 'required'
]);

Then you could do a foreach loop over the employee IDs, and construct your unique email rules. Assuming you have your Employee IDs in a variable like $ids and they are ordered the same way as your request data, you could do something like this:

<?php

$uniqueEmailRules = [];
foreach($ids as $key => $id) {
    $uniqueEmailRules['employees.' . $key . '.email'] = 'unique:employees,email,' . $id;
}

$request->validate($uniqueEmailRules);
Sign up to request clarification or add additional context in comments.

Comments

1

To avoid validating twice, you can do something like:

$request->validate([
    'employees.*.email' => Rule::forEach(function ($value, $attribute, $data) {
        $idKey = Str::replace('email', 'id', $attribute);
        return [
            'email', 'required', Rule::unique('employees')->ignore($data[$idKey])
        ];
    }),
]);

Within the callback, $value will give you the email address you're validating, $attribute will give you the key of the validation array eg employees.1.email and then $data gives you all of the validation entries for that rule.

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.