fix(user-profile): resolve profile by user uuid and auto-create when missing
GET/PATCH /api/v1/user-profile/{uuid} treated {uuid} as the profile's own
uuid, but clients pass the user's uuid — and a freshly OTP-registered user
has no profile row, so the call always 404'd. Add resolveProfile(): try
profile uuid, then user uuid → that user's profile, and (for the current
user or an admin) lazy-create an empty profile so the client always gets
an editable one. Foreign/unknown uuids still 404 with no leak.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace App\UserProfile\Controller;
|
||||
|
||||
use App\Auth\Entity\User;
|
||||
use App\Auth\Repository\UserRepository;
|
||||
use App\Shared\Constant\ErrorCodes;
|
||||
use App\Shared\Controller\BaseController;
|
||||
use App\UserProfile\Entity\UserProfile;
|
||||
@@ -20,6 +21,7 @@ class UserProfileController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserProfileRepository $repository,
|
||||
private readonly UserRepository $userRepository,
|
||||
) {}
|
||||
|
||||
#[Route('/api/v1/user-profile', methods: ['POST'])]
|
||||
@@ -40,7 +42,9 @@ class UserProfileController extends BaseController
|
||||
#[Route('/api/v1/user-profile/{uuid}', methods: ['GET'])]
|
||||
public function show(string $uuid, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$profile = $this->repository->findByUuid($uuid);
|
||||
// {uuid} may be a profile uuid or a user uuid; a user with no profile yet
|
||||
// gets an empty one created so the client always has an editable profile.
|
||||
$profile = $this->resolveProfile($uuid, $user, true);
|
||||
|
||||
if ($profile === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پروفایل یافت نشد', 404);
|
||||
@@ -56,7 +60,7 @@ class UserProfileController extends BaseController
|
||||
#[Route('/api/v1/user-profile/{uuid}', methods: ['PATCH'])]
|
||||
public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
|
||||
{
|
||||
$profile = $this->repository->findByUuid($uuid);
|
||||
$profile = $this->resolveProfile($uuid, $user, true);
|
||||
|
||||
if ($profile === null) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پروفایل یافت نشد', 404);
|
||||
@@ -88,6 +92,43 @@ class UserProfileController extends BaseController
|
||||
return $this->success(['message' => 'پروفایل با موفقیت حذف شد']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a profile from a uuid that may be the profile's own uuid or the
|
||||
* owning user's uuid. When $createIfMissing is true and the uuid belongs to
|
||||
* a user (the current user or, for admins, anyone) without a profile, an
|
||||
* empty profile is created and persisted.
|
||||
*/
|
||||
private function resolveProfile(string $uuid, User $currentUser, bool $createIfMissing): ?UserProfile
|
||||
{
|
||||
$profile = $this->repository->findByUuid($uuid);
|
||||
if ($profile !== null) {
|
||||
return $profile;
|
||||
}
|
||||
|
||||
$targetUser = $this->userRepository->findByUuid($uuid);
|
||||
if ($targetUser === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$profile = $this->repository->findByUser($targetUser);
|
||||
if ($profile !== null) {
|
||||
return $profile;
|
||||
}
|
||||
|
||||
if (!$createIfMissing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($targetUser->getId() !== $currentUser->getId() && !$currentUser->hasRole('ROLE_ADMIN')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$profile = new UserProfile($targetUser);
|
||||
$this->repository->save($profile);
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
private function canAccess(UserProfile $profile, User $currentUser): bool
|
||||
{
|
||||
return $profile->getUser()->getId() === $currentUser->getId()
|
||||
|
||||
Reference in New Issue
Block a user