(string) ($request->user()?->username ?? ''), ]); } public function store(Request $request): RedirectResponse { $normalized = UsernamePolicy::normalize((string) $request->input('username', '')); $request->merge(['username' => $normalized]); $validated = $request->validate([ 'username' => UsernameRequest::rulesFor((int) $request->user()->id), ], [ 'username.required' => 'Please choose a username to continue.', 'username.unique' => 'This username is already taken.', 'username.regex' => 'Use only letters, numbers, underscores, or hyphens.', 'username.min' => 'Username must be at least 3 characters.', 'username.max' => 'Username must be at most 20 characters.', ]); $candidate = (string) $validated['username']; $user = $request->user(); $similar = UsernamePolicy::similarReserved($candidate); if ($similar !== null && ! UsernamePolicy::hasApprovedOverride($candidate, (int) $user->id)) { $this->usernameApprovalService->submit($user, $candidate, 'onboarding_username', [ 'current_username' => (string) ($user->username ?? ''), ]); return back() ->withInput() ->with('status', 'Your request has been submitted for manual username review.') ->withErrors([ 'username' => 'This username is too similar to a reserved name and requires manual approval.', ]); } DB::transaction(function () use ($user, $candidate): void { $oldUsername = (string) ($user->username ?? ''); if ($oldUsername !== '' && strtolower($oldUsername) !== strtolower($candidate) && Schema::hasTable('username_history')) { DB::table('username_history')->insert([ 'user_id' => (int) $user->id, 'old_username' => strtolower($oldUsername), 'changed_at' => now(), 'created_at' => now(), 'updated_at' => now(), ]); } if ($oldUsername !== '' && strtolower($oldUsername) !== strtolower($candidate) && Schema::hasTable('username_redirects')) { DB::table('username_redirects')->updateOrInsert( ['old_username' => strtolower($oldUsername)], [ 'new_username' => strtolower($candidate), 'user_id' => (int) $user->id, 'created_at' => now(), 'updated_at' => now(), ] ); } $user->forceFill([ 'username' => strtolower($candidate), 'onboarding_step' => 'complete', 'username_changed_at' => now(), ])->save(); }); return redirect('/@' . strtolower($candidate)); } }