Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.2] In the Unique validation rule, allow ignoring an id using an array key #12612

Merged
merged 1 commit into from
Mar 28, 2016
Merged

[5.2] In the Unique validation rule, allow ignoring an id using an array key #12612

merged 1 commit into from
Mar 28, 2016

Conversation

themsaid
Copy link
Member

@themsaid themsaid commented Mar 3, 2016

As mentioned in this issue #12607.

While validating a value is unique inside an array of values there's currently no way to ignore a specific id for every record in the array.

$validator = Validator::make($request->all(), [
    'person.*.email' => 'email|unique:users,email,???,id',
]);

This PR will allow the following:

'person.*.email' => 'email|unique:users,email,[person.*.id],id'

Notes

  1. I introduced the syntax [array_key] to be able to separate between a value and a key.
  2. I understand that this approach will make the application hit the database for every single array record but I'm not sure what's the best way to hit the database only once and pass the result to the rule checker for every iteration. I didn't want to make changes in PresenceVerifier for now.

Verified

This commit was signed with the committer’s verified signature.
Eomm Manuel Spigolon
@taylorotwell
Copy link
Member

How does it hit the database for every key? Doesn't ->getValue only return a single value?

@themsaid
Copy link
Member Author

themsaid commented Mar 3, 2016

@taylorotwell I meant the check for uniqueness, It'll do the check for every record in the array.

So in this example, for every person record the PresenceVerifier::getCount will be executed and hit the database.

@taylorotwell
Copy link
Member

Hmm, I wonder if that could be a problem. For example if a malicious user sends 5,000 e-mails into the route, etc.

@themsaid
Copy link
Member Author

themsaid commented Mar 4, 2016

Yeah if 5,000 records were sent, the Validator will run PresenceVerifier::getCount() 5,000 times and hit the database for a query for each.

I guess we need to collect all unique rule values in a class property and make a method in the PresenceVerifier class that queries the database for all column-id values at once.

Something like:

public function getCountForArray($collection, $column, array $values, $idColumn = null, array $extra = [])
    {
        $query = $this->table($collection);

        $idColumn = $idColumn ?: 'id';

        $firstValue = array_first($values);

        $query->where(function ($q) use ($column, $idColumn, $values, $firstValue) {
            foreach ($values as $value) {
                $whereMethod = $value == $firstValue ? 'where' : 'orWhere';

                $q->$whereMethod([
                    [$column, '=', $value[$column]],
                    [$idColumn, '<>', $value['exclude']],
                ]);
            }
        });

        foreach ($extra as $key => $extraValue) {
            $this->addWhere($query, $key, $extraValue);
        }

        $query->select($column, $idColumn);

        $results = array_pluck($query->get(), $column);

        $output = [];

        foreach ($values as $value) {
            $output[$value[$column]] = in_array($value[$column], $results);
        }

        return $output;
    }

And then in the validator we call the presence verifier method only once at the beginning, and inside validateUnique we check for presence using the data we collected via getCountForArray.

Not sure if that's the best way to do it, this also will require much changes in the validator. What do you think?

@taylorotwell taylorotwell merged commit 8857f8a into laravel:5.2 Mar 28, 2016
@themsaid themsaid deleted the validate-unique-for-arrays branch April 1, 2016 20:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants