Skip to content

Commit a3068ba

Browse files
committed
Add username commands
1 parent 3964587 commit a3068ba

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Models\User;
6+
use App\Services\UsernameService;
7+
use Illuminate\Console\Command;
8+
9+
class CheckReservedUsernames extends Command
10+
{
11+
protected $signature = 'users:check-reserved
12+
{--show-all : Show all checked usernames, not just reserved ones}
13+
{--limit= : Limit the number of users to check}';
14+
15+
protected $description = 'Check if any users have reserved usernames';
16+
17+
public function handle(UsernameService $usernameService)
18+
{
19+
$this->info('Checking usernames against reserved list...');
20+
$this->newLine();
21+
22+
$query = User::query();
23+
24+
if ($limit = (int) $this->option('limit')) {
25+
$query->limit($limit);
26+
}
27+
28+
$users = $query->get();
29+
$reservedUsers = collect();
30+
31+
$progressBar = $this->output->createProgressBar($users->count());
32+
$progressBar->start();
33+
34+
foreach ($users as $user) {
35+
if ($usernameService->isReserved($user->username)) {
36+
$reservedUsers->push($user);
37+
} elseif ($this->option('show-all')) {
38+
$this->line("{$user->username} (ID: {$user->id}) - OK");
39+
}
40+
41+
$progressBar->advance();
42+
}
43+
44+
$progressBar->finish();
45+
$this->newLine(2);
46+
47+
if ($reservedUsers->isEmpty()) {
48+
$this->info('✓ No users found with reserved usernames!');
49+
50+
return Command::SUCCESS;
51+
}
52+
53+
$this->warn("Found {$reservedUsers->count()} user(s) with reserved usernames:");
54+
$this->newLine();
55+
56+
$this->table(
57+
['ID', 'Username', 'Email', 'Created At'],
58+
$reservedUsers->map(fn ($user) => [
59+
$user->id,
60+
$user->username,
61+
$user->email,
62+
$user->created_at->format('Y-m-d H:i:s'),
63+
])
64+
);
65+
66+
return Command::SUCCESS;
67+
}
68+
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use App\Models\Profile;
6+
use App\Models\User;
7+
use App\Services\UsernameService;
8+
use Illuminate\Console\Command;
9+
use Illuminate\Support\Facades\DB;
10+
11+
class UpdateReservedUsernames extends Command
12+
{
13+
protected $signature = 'users:update-reserved
14+
{--auto-suffix : Automatically add 4 random numbers to all reserved usernames}
15+
{--auto-user : Automatically change all to user + 10 random digits}';
16+
17+
protected $description = 'Check and update users with reserved usernames';
18+
19+
protected UsernameService $usernameService;
20+
21+
public function handle(UsernameService $usernameService)
22+
{
23+
$this->usernameService = $usernameService;
24+
25+
$this->info('Checking for users with reserved usernames...');
26+
$this->newLine();
27+
28+
$users = User::with('profile')->get();
29+
$reservedUsers = $users->filter(fn ($user) => $usernameService->isReserved($user->username)
30+
);
31+
32+
if ($reservedUsers->isEmpty()) {
33+
$this->info('✓ No users found with reserved usernames!');
34+
35+
return Command::SUCCESS;
36+
}
37+
38+
$this->warn("Found {$reservedUsers->count()} user(s) with reserved usernames.");
39+
$this->newLine();
40+
41+
$autoMode = $this->option('auto-suffix') ? 'suffix' :
42+
($this->option('auto-user') ? 'user' : null);
43+
44+
$updated = 0;
45+
$skipped = 0;
46+
47+
foreach ($reservedUsers as $user) {
48+
$result = $this->processUser($user, $autoMode);
49+
50+
if ($result === 'updated') {
51+
$updated++;
52+
} elseif ($result === 'skipped') {
53+
$skipped++;
54+
}
55+
56+
$this->newLine();
57+
}
58+
59+
$this->newLine();
60+
$this->info('Summary:');
61+
$this->line(" Updated: {$updated}");
62+
$this->line(" Skipped: {$skipped}");
63+
64+
return Command::SUCCESS;
65+
}
66+
67+
protected function processUser(User $user, ?string $autoMode): string
68+
{
69+
$this->line('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
70+
$this->warn('Reserved username found:');
71+
$this->line(" ID: {$user->id}");
72+
$this->line(" Username: {$user->username}");
73+
$this->line(" Email: {$user->email}");
74+
$this->newLine();
75+
76+
if ($autoMode) {
77+
$newUsername = $autoMode === 'suffix'
78+
? $this->generateSuffixUsername($user->username)
79+
: $this->generateUserUsername();
80+
81+
$this->updateUsername($user, $newUsername);
82+
$this->info("✓ Updated to: {$newUsername}");
83+
84+
return 'updated';
85+
}
86+
87+
$choice = $this->choice(
88+
'What would you like to do?',
89+
[
90+
'skip' => 'Skip this user',
91+
'auto-suffix' => 'Auto update (add 4 random numbers)',
92+
'auto-user' => 'Auto change (user + 10 random digits)',
93+
'manual' => 'Manual change',
94+
],
95+
'skip'
96+
);
97+
98+
switch ($choice) {
99+
case 'skip':
100+
$this->line('Skipped.');
101+
102+
return 'skipped';
103+
104+
case 'auto-suffix':
105+
$newUsername = $this->generateSuffixUsername($user->username);
106+
$this->updateUsername($user, $newUsername);
107+
$this->info("✓ Updated to: {$newUsername}");
108+
109+
return 'updated';
110+
111+
case 'auto-user':
112+
$newUsername = $this->generateUserUsername();
113+
$this->updateUsername($user, $newUsername);
114+
$this->info("✓ Updated to: {$newUsername}");
115+
116+
return 'updated';
117+
118+
case 'manual':
119+
return $this->handleManualChange($user);
120+
}
121+
122+
return 'skipped';
123+
}
124+
125+
protected function handleManualChange(User $user): string
126+
{
127+
while (true) {
128+
$newUsername = $this->ask('Enter new username (or "cancel" to skip)');
129+
130+
if (strtolower($newUsername) === 'cancel') {
131+
$this->line('Cancelled.');
132+
133+
return 'skipped';
134+
}
135+
136+
if (empty($newUsername)) {
137+
$this->error('Username cannot be empty.');
138+
139+
continue;
140+
}
141+
142+
if (! preg_match('/^[a-zA-Z0-9._-]+$/', $newUsername)) {
143+
$this->error('Username can only contain letters, numbers, dots, underscores, and hyphens.');
144+
145+
continue;
146+
}
147+
148+
if ($this->usernameService->isReserved($newUsername)) {
149+
$this->error('This username is also reserved. Please choose another.');
150+
151+
continue;
152+
}
153+
154+
if (User::where('username', $newUsername)->where('id', '!=', $user->id)->exists()) {
155+
$this->error('This username is already taken. Please choose another.');
156+
157+
continue;
158+
}
159+
160+
if ($this->confirm("Update username to '{$newUsername}'?", true)) {
161+
$this->updateUsername($user, $newUsername);
162+
$this->info("✓ Updated to: {$newUsername}");
163+
164+
return 'updated';
165+
}
166+
}
167+
}
168+
169+
protected function generateSuffixUsername(string $currentUsername): string
170+
{
171+
do {
172+
$suffix = str_pad((string) random_int(0, 9999), 4, '0', STR_PAD_LEFT);
173+
$newUsername = $currentUsername.$suffix;
174+
} while (
175+
User::where('username', $newUsername)->exists() ||
176+
$this->usernameService->isReserved($newUsername)
177+
);
178+
179+
return $newUsername;
180+
}
181+
182+
protected function generateUserUsername(): string
183+
{
184+
do {
185+
$randomDigits = '';
186+
for ($i = 0; $i < 10; $i++) {
187+
$randomDigits .= random_int(0, 9);
188+
}
189+
$newUsername = 'user'.$randomDigits;
190+
} while (
191+
User::where('username', $newUsername)->exists() ||
192+
$this->usernameService->isReserved($newUsername)
193+
);
194+
195+
return $newUsername;
196+
}
197+
198+
protected function updateUsername(User $user, string $newUsername): void
199+
{
200+
DB::transaction(function () use ($user, $newUsername) {
201+
$user->username = $newUsername;
202+
$user->save();
203+
204+
if ($user->profile_id) {
205+
$profile = Profile::find($user->profile_id);
206+
if ($profile) {
207+
$profile->username = $newUsername;
208+
$profile->save();
209+
}
210+
}
211+
});
212+
}
213+
}

0 commit comments

Comments
 (0)