0
votes

I'm looking for the best way to enforce unique fields in Laravel form requests, except for when editing. The most obvious example would be only allowing an email address to be used once i.e. each user in a system would need a unique email address.

This is straight-forward to achieve using form requests, by making the field unique:

public function rules()
{
    return [
        'email' => ['required', 'email', 'unique:users,email'],
    ];
}

This makes the field required, validates as an email and makes it unique in the user's table. Perfect for creating a new user. But what about editing? It's totally plausible for a user to want to edit their profile and save the same email address again (which is totally valid). But, of course, the unique rule will fire because the email is already "in use" (albeit by the user trying to edit themselves).

Laravel offers a way around this, by passing in the ID of the user table to ignore:

'email' => ['required', 'email', 'unique:users,email,'.$userId]

But how to get the user ID? Maybe this is a bad example because it's trivial to get the logged in user ID, but what about other objects that aren't stored in the session?

If one is using REST, then the ID of the object would be in the URL and available using the route:

$userId = $this->route('order');

But this seems very dirty. And what about other instances where you're not using REST to modify an object? How do you guys enforce uniqueness in form requests when editing objects in Laravel 5?

1

1 Answers

1
votes

You can have 2 form requests with different rules if you want. I don't think it is dirty to pull information from the FormRequest to adjust the rules as the FormRequest is a Request and you may need some of that information to decide how to do the validation or authorization.

I have a post about using a FormRequest for create and update, using a unique rule in the rules, that uses a route parameter to append to the unique rule. asklagbox - blog - Form Request for Store and Update

In that it uses the HTTP method to know if it is for store or update and appends to the unique rule if it is an update happening (Rest). If you weren't using REST you could still do this you would just need to check something besides the HTTP Method to know if this is for store or update. You could adjust this method to check an input value instead of a route parameter for appending to the unique rule if you wanted as well. Since the FormRequest is pretty specific to the resource you don't really need to go crazy and think of every single possible other way you want to do something, or your app/API could start to become inconsistent.

// added to base FormRequest

protected $updateMethods = ['PUT', 'PATCH'];

protected function appendUnique($key, $keyName = 'id')
{
    if (in_array($this->method(), $this->updateMethods) && $param = $this->route($key)) {
        if ($param instanceof Model) {
            $rt = "{$param->getKey()},{$param->getKeyName()}";
        } else {
            $rt = "{$param},{$keyName}";
        }
        return ','. $rt;
    }
}

Then the array in the rules method of your FormRequest would have something like this:

'email' => 'required|unique:users,email'. $this->appendUnique('user'),

I use Route Model Bindings (implicit and explicit) often which is why this is setup like this and rarely would have the resource I am checking against be passed via an input and not a route parameter.

I hope this helps.