Laravel Validation with Soft Deleted Model and Unique DB field

Viewed 5294

I am trying to wrap my head around how to handle this use case.

I have a user model that has a unique db field (email). I am also using soft deletes.

I need to handle three cases:

1) The user model is updated. At the moment my validation stops the user from being updated, because ofcourse this email already exists. I think this is easily solved by ignoring the email validation if the email has not been changed.

2) A different user model is updated and the email is changed to another email already present in the db.

3) A new model is created and the email is already present in the db.

All the above scenarios must also take into account soft deleted models.

Would someone have some advice on how to structure this?

4 Answers

For updating you can adjust your validation rules to ignore the current user record when updating. The rule signature is this:

'email' => 'unique:user,email,{$userId},id,deleted_at,NULL'

For inserting new, a rule like this:

'email' => 'unique:user,email,NULL,NULL,deleted_at,NULL'

Should work and ignore softDeleted records. If will still fail if you have a unique key on your database.

Of course, if you also undelete a record, you will have duplicates, which Laravel can not prevent.

You can use the Rule::unique() validation. Either get your user model out from the route if your using model binding, or do a traditional db query to get the user out by an ID (if your passing one in). Then you can do this in the validation.

This should accommodate soft deleted models too.

return [
    'email' => Rule::unique('users', 'email')->ignore($user->email)->whereNull('deleted_at')->orWhereNotNull('deleted_at')
]

For Laravel 8, the above solutions doesn't work anymore, probably the unique rule was changed down the road.

However, you can go with the following validation rule.

use Illuminate\Validation\Rule;

'email' => [
    'required',
    'email',
    Rule::unique('users', 'email')->where(function($query){
       return $query->whereNull('deleted_at')
         ->orWhereNotNull('deleted_at');
    })
]

In Laravel 8 it is greatly simplified using withoutTrashed() rule modifier:

use Illuminate\Validation\Rule;

'email' => [
    'required',
    'email',
    Rule::unique('users', 'email')->withoutTrashed()
]

The method is mentioned in the L8 API:

https://laravel.com/api/8.x/Illuminate/Validation/Rules/Unique.html#method_withoutTrashed


Is it probably also worth mentioning that there is a Rule::exists()->withoutTrashed() available as well mentioned in the L8 API too:

https://laravel.com/api/8.x/Illuminate/Validation/Rules/Exists.html#method_withoutTrashed

Related