We have exciting plans for Picstome to enhance its utility for photographers, particularly in showcasing work such as portfolios or external links like biolinks. However, we currently lack an option for users to have a handle like `picstome.com/@oliver`, so we needed to address this issue first.
My plan was to generate a unique username for every new user registration based on the provided name, which they could later change in the branding settings.
I decided to create a new service class to manage the logic for generating a unique handle. This involves checking if a user handle already exists and appending an incremental numeric value if it does.
// app/Services/HandleGenerationService.php class HandleGenerationService { public function generateFromName(string $name): string { return Str::slug($name, ''); } public function generateUniqueHandle(string $name): string { $baseHandle = $this->generateFromName($name); $handle = $baseHandle; $counter = 1; while (Team::where('handle', $handle)->exists()) { $handle = $baseHandle . $counter; $counter++; } return $handle; } }
Next, since we already have registered users, we needed to automatically generate handles for all existing users.
To accomplish this, I created an artisan command to find users without handles and generate a unique handle for each. Each user has a personal team, so I needed to update the personal team handle with an autogenerated one.
// app/Console/Commands/BackfillTeamHandlesCommand.php class BackfillTeamHandlesCommand extends Command { protected $signature = 'teams:backfill-handles'; protected $description = 'Backfill handles for existing teams that don\'t have one'; public function handle() { $this->info('Backfilling handles for existing teams...'); $teamsWithoutHandles = Team::whereNull('handle')->get(); if ($teamsWithoutHandles->isEmpty()) { $this->info('No teams found that need handles'); $this->info('Backfill completed successfully'); return; } $handleGenerator = new HandleGenerationService(); $processed = 0; foreach ($teamsWithoutHandles as $team) { $team->update(['handle' => $handleGenerator->generateUniqueHandle($team->name)]); $processed++; } $this->info("Processed {$processed} teams"); $this->info('Backfill completed successfully'); } }
Creating a service class was beneficial because I used it to generate unique handles for existing users.
Now that all users have handles, I focused on allowing them to change their user handles whenever they wish.
Updating the handle value is straightforward but requires strict validation rules. The most crucial rule is that the user handle must not be used by another user.
// app/Livewire/Forms/BrandingForm.php class BrandingForm extends Form { //... public function rules() { return [ 'handle' => [ 'required', 'string', 'lowercase', 'min:2', 'max:50', 'regex:/^[a-z0-9]+$/', 'unique:teams,handle,' . ($this->team->id ?? 'null'), ], ]; } }
Lastly, I needed to register a new route to manage user handles. This is the first step toward adding more functionality for user handles, and it simply displays a welcome page.
// routes/web.php Route::get('/@{handle}', [HandleController::class, 'show']) ->where('handle', '[a-zA-Z0-9_]+') ->name('handle.show');
// app/Http/Controllers/HandleController.php class HandleController extends Controller { public function show(Request $request, string $handle) { $team = Team::where('handle', strtolower($handle))->first(); abort_if(!$team, 404); return view('team.show', ['team' => $team]); } }
Now, I have the minimal functionality to manage user handles. My next task will be to implement showcasing galleries as portfolios and displaying bio links on the public handle page.
You can also check our project repository to view our source code; this implementation may be useful for your projects.