feat(profile): enforce uniqueness of national_code across user profiles and update related error handling

This commit is contained in:
hamed
2026-06-24 11:52:13 +03:30
parent 3eb82ffa7d
commit 650e36bce2
9 changed files with 247 additions and 7 deletions
@@ -91,6 +91,11 @@ class UserProfileController extends BaseController
}
$data = json_decode($request->getContent(), true) ?? [];
if ($error = $this->guardNationalCode($data, null)) {
return $error;
}
$profile = new UserProfile($user);
$this->hydrate($profile, $data);
$this->repository->save($profile);
@@ -131,12 +136,8 @@ class UserProfileController extends BaseController
$data = json_decode($request->getContent(), true) ?? [];
if (array_key_exists('national_code', $data) && $data['national_code'] !== null && $data['national_code'] !== '') {
$code = InputValidator::toEnglishDigits((string) $data['national_code']);
if (!InputValidator::isValidIranNationalCode($code)) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'کد ملی نامعتبر است', 422, 'national_code');
}
$data['national_code'] = $code;
if ($error = $this->guardNationalCode($data, $profile)) {
return $error;
}
$this->hydrate($profile, $data);
@@ -202,6 +203,36 @@ class UserProfileController extends BaseController
|| $currentUser->hasRole('ROLE_ADMIN');
}
/**
* Normalize, format-validate and uniqueness-check national_code in $data.
* Mutates $data['national_code'] to the latin-digit form. Returns an error
* response if invalid or already taken by another profile, otherwise null.
*/
private function guardNationalCode(array &$data, ?UserProfile $current): ?JsonResponse
{
if (!array_key_exists('national_code', $data) || $data['national_code'] === null || $data['national_code'] === '') {
return null;
}
$code = InputValidator::toEnglishDigits((string) $data['national_code']);
if (!InputValidator::isValidIranNationalCode($code)) {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'کد ملی نامعتبر است', 422, 'national_code');
}
$data['national_code'] = $code;
$existing = $this->repository->findOneByNationalCode($code);
if ($existing !== null && ($current === null || $existing->getUuid() !== $current->getUuid())) {
return $this->error(
ErrorCodes::ERR_PROFILE_NATIONAL_CODE_TAKEN,
'این کد ملی قبلاً برای کاربر دیگری ثبت شده است',
409,
'national_code'
);
}
return null;
}
private function hydrate(UserProfile $profile, array $data): void
{
if (array_key_exists('name', $data)) $profile->setLabel($data['name']);
+1 -1
View File
@@ -9,7 +9,7 @@ use Symfony\Component\Uid\Uuid;
#[ORM\Entity]
#[ORM\Table(name: 'profiles')]
#[ORM\UniqueConstraint(name: 'idx_profiles_user', columns: ['user_id'])]
#[ORM\Index(columns: ['national_code'], name: 'idx_profiles_national_code')]
#[ORM\UniqueConstraint(name: 'uniq_profiles_national_code', columns: ['national_code'])]
class UserProfile
{
#[ORM\Id]
@@ -24,6 +24,11 @@ class UserProfileRepository extends ServiceEntityRepository
return $this->findOneBy(['uuid' => $uuid]);
}
public function findOneByNationalCode(string $nationalCode): ?UserProfile
{
return $this->findOneBy(['nationalCode' => $nationalCode]);
}
public function save(UserProfile $profile, bool $flush = true): void
{
$this->getEntityManager()->persist($profile);