diff --git a/app/Actions/Fortify/UpdateUserProfileInformation.php b/app/Actions/Fortify/UpdateUserProfileInformation.php index 4950d2ee4..d7ffd134a 100644 --- a/app/Actions/Fortify/UpdateUserProfileInformation.php +++ b/app/Actions/Fortify/UpdateUserProfileInformation.php @@ -2,10 +2,11 @@ namespace App\Actions\Fortify; +use App\Rules\UniqueUserEmail; use App\Traits\UserEmailVerification; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Support\Facades\Validator; -use Illuminate\Validation\Rule; +use Illuminate\Support\Str; use Laravel\Fortify\Contracts\UpdatesUserProfileInformation; class UpdateUserProfileInformation implements UpdatesUserProfileInformation @@ -25,12 +26,12 @@ public function update($user, array $input): void 'string', 'email', 'max:255', - Rule::unique('users')->ignore($user->id), + new UniqueUserEmail($user->id), ], ])->validateWithBag('updateProfileInformation'); if ( - $input['email'] !== $user->email && + Str::lower($input['email']) !== $user->email && $user instanceof MustVerifyEmail ) { $this->updateVerifiedUser($user, $input['email']); diff --git a/app/Http/Controllers/IndividualController.php b/app/Http/Controllers/IndividualController.php index 876fe778b..57e9e274b 100644 --- a/app/Http/Controllers/IndividualController.php +++ b/app/Http/Controllers/IndividualController.php @@ -31,6 +31,7 @@ use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Str; use Spatie\LaravelOptions\Options; class IndividualController extends Controller @@ -307,7 +308,7 @@ public function updateCommunicationAndConsultationPreferences(UpdateIndividualCo if ( $data['email'] !== '' - && $data['email'] !== $user->email + && Str::lower($data['email']) !== $user->email && $user instanceof MustVerifyEmail ) { $this->updateVerifiedUser($user, $data['email']); diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index aafda7f0a..422c9f1e7 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -30,6 +30,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cookie; use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Str; use Spatie\LaravelOptions\Options; class SettingsController extends Controller @@ -196,7 +197,7 @@ public function updateCommunicationAndConsultationPreferences(UpdateCommunicatio $individual = $user->individual; if ( - isset($data['email']) && $data['email'] !== $user->email && $user instanceof MustVerifyEmail + isset($data['email']) && Str::lower($data['email']) !== $user->email && $user instanceof MustVerifyEmail ) { $this->updateVerifiedUser($user, $data['email']); } diff --git a/app/Http/Requests/UpdateCommunicationAndConsultationPreferencesRequest.php b/app/Http/Requests/UpdateCommunicationAndConsultationPreferencesRequest.php index 2fb76f093..7c27f5bf0 100644 --- a/app/Http/Requests/UpdateCommunicationAndConsultationPreferencesRequest.php +++ b/app/Http/Requests/UpdateCommunicationAndConsultationPreferencesRequest.php @@ -5,6 +5,7 @@ use App\Enums\ContactPerson; use App\Enums\EngagementFormat; use App\Enums\MeetingType; +use App\Rules\UniqueUserEmail; use App\Traits\ConditionallyRequireContactMethods; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; @@ -32,7 +33,7 @@ public function rules(): array 'string', 'email', 'max:255', - Rule::unique('users')->ignore($this->user()->id), + new UniqueUserEmail($this->user()->id), ], 'phone' => 'required_if:vrs,true|nullable|phone:CA', 'vrs' => 'nullable|boolean', diff --git a/app/Http/Requests/UpdateIndividualCommunicationAndConsultationPreferencesRequest.php b/app/Http/Requests/UpdateIndividualCommunicationAndConsultationPreferencesRequest.php index 1210f5faf..b5874d88e 100644 --- a/app/Http/Requests/UpdateIndividualCommunicationAndConsultationPreferencesRequest.php +++ b/app/Http/Requests/UpdateIndividualCommunicationAndConsultationPreferencesRequest.php @@ -4,9 +4,9 @@ use App\Enums\ContactPerson; use App\Enums\MeetingType; +use App\Rules\UniqueUserEmail; use App\Traits\ConditionallyRequireContactMethods; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\Validator; @@ -31,7 +31,7 @@ public function rules(): array 'string', 'email', 'max:255', - Rule::unique('users')->ignore($this->user()->id), + new UniqueUserEmail($this->user()->id), ], 'phone' => 'required_if:vrs,true|nullable|phone:CA', 'vrs' => 'nullable|boolean', diff --git a/app/Rules/UniqueUserEmail.php b/app/Rules/UniqueUserEmail.php index 1d0b86779..ed2d2affa 100644 --- a/app/Rules/UniqueUserEmail.php +++ b/app/Rules/UniqueUserEmail.php @@ -8,9 +8,28 @@ class UniqueUserEmail implements InvokableRule { + public $id; + + public $idColumn; + + // Can use the $id and $idColumn to define a model to ignore. Similar to how Laravel's unique validation rule works + // see: https://laravel.com/docs/9.x/validation#rule-unique + public function __construct(mixed $id = null, ?string $idColumn = null) + { + $this->id = $id; + $this->idColumn = $idColumn ?? 'id'; + } + public function __invoke($attribute, mixed $value, $fail): void { - if (User::whereBlind($attribute, $attribute.'_index', Str::lower($value))->exists()) { + $exists = User::whereBlind($attribute, $attribute.'_index', Str::lower($value)) + ->when($this->id, function ($query, $role) { + $query->whereNot(function ($query) { + $query->where($this->idColumn, $this->id); + }); + }) + ->exists(); + if ($exists) { $fail(__('A user with this email already exists.')); } }