diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php
index a300bfaf..660e151b 100644
--- a/app/Http/Controllers/Auth/VerifyEmailController.php
+++ b/app/Http/Controllers/Auth/VerifyEmailController.php
@@ -3,7 +3,7 @@
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
-use Illuminate\Auth\Events\Verified;
+use App\Jobs\VerifyUserEmail;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\RedirectResponse;
@@ -14,16 +14,14 @@ class VerifyEmailController extends Controller
*/
public function __invoke(EmailVerificationRequest $request): RedirectResponse
{
- if ($request->user()->hasVerifiedEmail()) {
+ /** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
+ $user = $request->user();
+
+ if ($user->hasVerifiedEmail()) {
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
- if ($request->user()->markEmailAsVerified()) {
- /** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */
- $user = $request->user();
-
- event(new Verified($user));
- }
+ dispatch(new VerifyUserEmail($user));
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
}
diff --git a/app/Jobs/VerifyUserEmail.php b/app/Jobs/VerifyUserEmail.php
new file mode 100644
index 00000000..bbab6695
--- /dev/null
+++ b/app/Jobs/VerifyUserEmail.php
@@ -0,0 +1,69 @@
+user instanceof MustVerifyEmail) {
+ return;
+ }
+
+ DB::transaction(function (): void {
+ $this->purgeConflicts();
+ $this->applyVerification();
+ });
+
+ event(new Verified($this->user));
+ }
+
+ /**
+ * Remove conflicting unverified users and reservations.
+ */
+ private function purgeConflicts(): void
+ {
+ $email = $this->user->unverified_email ?? $this->user->email;
+
+ User::query()
+ ->where('email', $email)
+ ->whereNot('id', $this->user->id)
+ ->whereNull('email_verified_at')
+ ->delete();
+
+ User::query()
+ ->where('unverified_email', $email)
+ ->whereNot('id', $this->user->id)
+ ->whereNull('email_verified_at')
+ ->update(['unverified_email' => null]);
+ }
+
+ /**
+ * Apply email change or mark as verified.
+ */
+ private function applyVerification(): void
+ {
+ if ($this->user->unverified_email) {
+ $this->user->forceFill([
+ 'email' => $this->user->unverified_email,
+ 'unverified_email' => null,
+ 'email_verified_at' => now(),
+ ])->save();
+
+ return;
+ }
+
+ $this->user->forceFill([
+ 'email_verified_at' => now(),
+ ])->save();
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index 3cb5ccb1..5b08c8f2 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -2,13 +2,13 @@
namespace App\Models;
-// use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
-class User extends Authenticatable
+class User extends Authenticatable implements MustVerifyEmail
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
@@ -21,6 +21,7 @@ class User extends Authenticatable
protected $fillable = [
'name',
'email',
+ 'unverified_email',
'password',
];
diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php
index 05fb5d9e..a3c874a5 100644
--- a/database/migrations/0001_01_01_000000_create_users_table.php
+++ b/database/migrations/0001_01_01_000000_create_users_table.php
@@ -16,6 +16,7 @@ public function up(): void
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
+ $table->string('unverified_email')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
diff --git a/resources/views/livewire/settings/profile.blade.php b/resources/views/livewire/settings/profile.blade.php
index cb088335..a92abf3d 100644
--- a/resources/views/livewire/settings/profile.blade.php
+++ b/resources/views/livewire/settings/profile.blade.php
@@ -8,7 +8,7 @@
new class extends Component {
public string $name = '';
- public string $email = '';
+ public string $unverified_email = '';
/**
* Mount the component.
@@ -16,7 +16,7 @@
public function mount(): void
{
$this->name = Auth::user()->name;
- $this->email = Auth::user()->email;
+ $this->unverified_email = Auth::user()->unverified_email ?? Auth::user()->email;
}
/**
@@ -29,19 +29,23 @@ public function updateProfileInformation(): void
$validated = $this->validate([
'name' => ['required', 'string', 'max:255'],
- 'email' => [
+ 'unverified_email' => [
'required',
'string',
'lowercase',
'email',
'max:255',
- Rule::unique(User::class)->ignore($user->id)
+ Rule::unique(User::class, 'email')->ignore($user->id)
],
]);
+ if ($validated['unverified_email'] === Auth::user()->email) {
+ unset($validated['unverified_email']);
+ }
+
$user->fill($validated);
- if ($user->isDirty('email')) {
+ if ($user->isDirty('unverified_email')) {
$user->email_verified_at = null;
}
@@ -77,7 +81,7 @@ public function resendVerificationNotification(): void