feat: enhance DoctorAddress entity to support clinic addresses and types

- Added `clinic_id` and `type` fields to `DoctorAddress` entity to differentiate between personal and clinic addresses.
- Updated constructor to support creation of addresses for both doctors and clinics.
- Modified repository methods to handle new address types and added methods for counting and finding addresses by clinic.
- Implemented migration to update the database schema accordingly.
- Removed deprecated endpoint for creating addresses from clinics and updated related controller methods.
- Added new endpoints for managing clinic addresses, including CRUD operations.
- Updated frontend components to handle new address types and display accordingly.
This commit is contained in:
hamed
2026-06-12 13:39:37 +03:30
parent 63073c6a42
commit 0333b24071
13 changed files with 1446 additions and 89 deletions
+117 -9
View File
@@ -7,6 +7,8 @@ use App\Auth\Repository\UserRepository;
use App\Clinic\Entity\Clinic;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorAddress;
use App\Doctor\Repository\DoctorAddressRepository;
use App\Doctor\Repository\DoctorRepository;
use App\DoctorService\Repository\DoctorServiceRepository;
use App\Insurance\Repository\InsuranceRepository;
@@ -28,16 +30,17 @@ use Symfony\Component\Uid\Uuid;
class ClinicController extends BaseController
{
public function __construct(
private readonly ClinicRepository $clinicRepo,
private readonly DoctorRepository $doctorRepo,
private readonly SpecialtyRepository $specialtyRepo,
private readonly ClinicRepository $clinicRepo,
private readonly DoctorRepository $doctorRepo,
private readonly DoctorAddressRepository $addressRepo,
private readonly SpecialtyRepository $specialtyRepo,
private readonly DoctorServiceRepository $serviceRepo,
private readonly InsuranceRepository $insuranceRepo,
private readonly ProvinceRepository $provinceRepo,
private readonly CityRepository $cityRepo,
private readonly UserRepository $userRepo,
private readonly FileValidatorService $fileValidator,
private readonly string $projectDir,
private readonly InsuranceRepository $insuranceRepo,
private readonly ProvinceRepository $provinceRepo,
private readonly CityRepository $cityRepo,
private readonly UserRepository $userRepo,
private readonly FileValidatorService $fileValidator,
private readonly string $projectDir,
) {}
#[OA\Post(
@@ -535,4 +538,109 @@ class ClinicController extends BaseController
return $this->error(ErrorCodes::ERR_VALIDATION_001, $e->getMessage(), 422);
}
}
// ── Clinic Addresses ─────────────────────────────────────────────────────
#[Route('/api/v1/clinic/{clinicUuid}/addresses', methods: ['GET'])]
public function listAddresses(string $clinicUuid): JsonResponse
{
$clinic = $this->clinicRepo->findByUuid($clinicUuid);
if ($clinic === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
}
$addresses = $this->addressRepo->findBy(['clinicId' => $clinic->getId()]);
return $this->success(['data' => array_map(fn(DoctorAddress $a) => $a->toArray(), $addresses)]);
}
#[Route('/api/v1/clinic/{clinicUuid}/address', methods: ['POST'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function createAddress(string $clinicUuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
$clinic = $this->clinicRepo->findByUuid($clinicUuid);
if ($clinic === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
}
if ($clinic->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
}
$data = json_decode($request->getContent(), true) ?? [];
$address = DoctorAddress::forClinic($clinic->getId());
$this->hydrateClinicAddress($address, $data);
$this->addressRepo->save($address);
return $this->success(['data' => $address->toArray()], 201);
}
#[Route('/api/v1/clinic/{clinicUuid}/address/{addressUuid}', methods: ['PATCH'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function updateAddress(string $clinicUuid, string $addressUuid, Request $request, #[CurrentUser] User $user): JsonResponse
{
$clinic = $this->clinicRepo->findByUuid($clinicUuid);
if ($clinic === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
}
if ($clinic->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
}
$address = $this->addressRepo->findByUuidAndClinic($addressUuid, $clinic->getId());
if ($address === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'آدرس یافت نشد', 404);
}
$data = json_decode($request->getContent(), true) ?? [];
$this->hydrateClinicAddress($address, $data);
$this->addressRepo->save($address);
return $this->success(['data' => $address->toArray()]);
}
#[Route('/api/v1/clinic/{clinicUuid}/address/{addressUuid}', methods: ['DELETE'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
public function deleteAddress(string $clinicUuid, string $addressUuid, #[CurrentUser] User $user): JsonResponse
{
$clinic = $this->clinicRepo->findByUuid($clinicUuid);
if ($clinic === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404);
}
if ($clinic->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
}
$address = $this->addressRepo->findByUuidAndClinic($addressUuid, $clinic->getId());
if ($address === null) {
return $this->error(ErrorCodes::ERR_VALIDATION_002, 'آدرس یافت نشد', 404);
}
$remaining = $this->addressRepo->countByClinic($clinic->getId());
if ($remaining <= 1) {
return $this->error(ErrorCodes::ERR_CONFLICT_001, 'کلینیک باید حداقل یک آدرس داشته باشد', 409);
}
$this->addressRepo->remove($address);
return $this->success(['message' => 'آدرس با موفقیت حذف شد']);
}
private function hydrateClinicAddress(DoctorAddress $address, array $data): void
{
if (array_key_exists('name', $data)) $address->setName($data['name']);
if (array_key_exists('address', $data)) $address->setAddress($data['address']);
if (array_key_exists('telephone', $data)) $address->setTelephone($data['telephone']);
if (array_key_exists('latitude', $data)) $address->setLatitude($data['latitude'] !== null ? (float) $data['latitude'] : null);
if (array_key_exists('longitude', $data)) $address->setLongitude($data['longitude'] !== null ? (float) $data['longitude'] : null);
if (array_key_exists('city_id', $data)) {
$address->setCity($data['city_id'] !== null ? $this->cityRepo->find((int) $data['city_id']) : null);
}
if (array_key_exists('province_id', $data)) {
$address->setProvince($data['province_id'] !== null ? $this->provinceRepo->find((int) $data['province_id']) : null);
}
}
}