diff --git a/docs/api/clinic.md b/docs/api/clinic.md index a9e0ced3..ecef902c 100644 --- a/docs/api/clinic.md +++ b/docs/api/clinic.md @@ -157,7 +157,7 @@ Get clinic detail. } ``` -> `city`/`state`/`map` are resolved from the clinic's **address** (`DoctorAddress` linked by `clinic_id`), not from columns on the clinic. Each is an array with a single object (or empty `[]` if the clinic has no address). `doctors` is a **count**; the actual doctor list comes from `GET /api/v1/clinic/doctor-list/{clinicUuid}` (`doctor_list` here is always `null`). +> `city`/`state`/`map`/`location`/`phone`/`phone_number` are all resolved from the clinic's **address** (`DoctorAddress` linked by `clinic_id`), not from columns on the clinic. `location` and `phone`/`phone_number` fall back to the deprecated `clinics.address` / `clinics.telephone` columns only when the address record has no value — reading them from different rows made one response describe two different places. Each is an array with a single object (or empty `[]` if the clinic has no address). `doctors` is a **count**; the actual doctor list comes from `GET /api/v1/clinic/doctor-list/{clinicUuid}` (`doctor_list` here is always `null`). ### معنای `is_active` @@ -265,6 +265,7 @@ List clinics with pagination. | `doctors_count` | integer | Number of doctors linked to the clinic | | `city` | string\|null | City name, resolved from the clinic's address (`DoctorAddress`) | | `state` | string\|null | Province name, resolved from the clinic's address (`DoctorAddress`) | +| `phone` / `phone_number` | string\|null | Contact number from the clinic's address (`DoctorAddress`), falling back to the deprecated `clinics.telephone` column | | `24_7` | boolean | Open 24/7 flag | | `field_working_days` | string\|null | Working days/hours description | diff --git a/src/Clinic/Controller/ClinicController.php b/src/Clinic/Controller/ClinicController.php index 4f785f0c..47f73e01 100644 --- a/src/Clinic/Controller/ClinicController.php +++ b/src/Clinic/Controller/ClinicController.php @@ -160,9 +160,9 @@ class ClinicController extends BaseController return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404); } - [$stateData, $cityData, $map] = $this->loadLocationData($clinic); + [$stateData, $cityData, $map, $street, $telephone] = $this->loadLocationData($clinic); - return $this->success(['data' => $clinic->toDetailArray($stateData, $cityData, $map)]); + return $this->success(['data' => $clinic->toDetailArray($stateData, $cityData, $map, $street, $telephone)]); } #[OA\Patch( @@ -237,9 +237,9 @@ class ClinicController extends BaseController $this->hydrateClinic($clinic, $data); $this->clinicRepo->save($clinic); - [$stateData, $cityData, $map] = $this->loadLocationData($clinic); + [$stateData, $cityData, $map, $street, $telephone] = $this->loadLocationData($clinic); - return $this->success(['data' => $clinic->toDetailArray($stateData, $cityData, $map)]); + return $this->success(['data' => $clinic->toDetailArray($stateData, $cityData, $map, $street, $telephone)]); } #[OA\Get( @@ -284,8 +284,8 @@ class ClinicController extends BaseController return $this->paginated( array_map(function (Clinic $c) use ($locations) { - $loc = $locations[$c->getId()] ?? ['city' => null, 'state' => null]; - return $c->toListArray($loc['city'], $loc['state']); + $loc = $locations[$c->getId()] ?? ['city' => null, 'state' => null, 'telephone' => null]; + return $c->toListArray($loc['city'], $loc['state'], $loc['telephone']); }, $result['items']), $result['total'], $result['page'], @@ -612,11 +612,17 @@ class ClinicController extends BaseController $provinceData = []; $cityData = []; $map = ['latitude' => null, 'longitude' => null]; + $street = null; + $telephone = null; // City/province/coordinates live on the clinic's address (DoctorAddress // linked by clinicId), not on the deprecated columns of the clinic itself. + // The street text and phone come from the same record, otherwise one + // response mixes two rows: city from the address, street from the clinic. $address = $this->addressRepo->findOneByClinic((int) $clinic->getId()); if ($address !== null) { + $street = $address->getAddress(); + $telephone = $address->getTelephone(); $province = $address->getProvince(); if ($province !== null) { $provinceData = ['uuid' => $province->getUuid(), 'id' => (string) $province->getId(), 'name' => $province->getName()]; @@ -636,7 +642,7 @@ class ClinicController extends BaseController ]; } - return [$provinceData, $cityData, $map]; + return [$provinceData, $cityData, $map, $street, $telephone]; } private function handleFileUpload(Request $request, string $subDir): JsonResponse diff --git a/src/Clinic/Entity/Clinic.php b/src/Clinic/Entity/Clinic.php index c0c8df38..74806eb5 100644 --- a/src/Clinic/Entity/Clinic.php +++ b/src/Clinic/Entity/Clinic.php @@ -187,20 +187,33 @@ class Clinic private function touch(): void { $this->updatedAt = time(); } - public function toDetailArray(array $provinceData = [], array $cityData = [], ?array $map = null): array - { + /** + * $street/$telephone come from the clinic's DoctorAddress record. The columns + * on this entity are the deprecated legacy source and are only a fallback: + * mixing the two makes a single response describe two different places. + */ + public function toDetailArray( + array $provinceData = [], + array $cityData = [], + ?array $map = null, + ?string $street = null, + ?string $telephone = null + ): array { + $phone = $this->firstFilled($telephone, $this->telephone); + $address = $this->firstFilled($street, $this->address); + return [ 'id' => (string) $this->id, 'uuid' => $this->uuid, 'name' => $this->name, 'title' => $this->name, 'is_active' => $this->isActive, - 'phone' => $this->telephone, + 'phone' => $phone, 'logo' => $this->clinicLogo, 'images_clinic' => $this->imagesClinic ?? [], 'social_media' => $this->socialMedia, 'clinic_logo' => $this->clinicLogo, - 'phone_number' => $this->telephone, + 'phone_number' => $phone, 'caption' => $this->info, 'list_bime' => array_map(fn(Insurance $i) => [ 'uuid' => $i->getUuid(), 'id' => (string) $i->getId(), 'name' => $i->getName(), @@ -220,7 +233,7 @@ class Clinic 'doctor_list' => null, 'city' => $cityData ? [$cityData] : [], 'state' => $provinceData ? [$provinceData] : [], - 'location' => $this->address, + 'location' => $address, 'map' => $map ?? [ 'latitude' => $this->latitude !== null ? (string) $this->latitude : null, 'longitude' => $this->longitude !== null ? (string) $this->longitude : null, @@ -230,15 +243,28 @@ class Clinic ]; } - public function toListArray(?string $city = null, ?string $state = null): array + private function firstFilled(?string ...$values): ?string { + foreach ($values as $value) { + if ($value !== null && trim($value) !== '') { + return $value; + } + } + + return null; + } + + public function toListArray(?string $city = null, ?string $state = null, ?string $telephone = null): array + { + $phone = $this->firstFilled($telephone, $this->telephone); + return [ 'id' => (string) $this->id, 'uuid' => $this->uuid, 'name' => $this->name, 'title' => $this->name, - 'phone' => $this->telephone, - 'phone_number' => $this->telephone, + 'phone' => $phone, + 'phone_number' => $phone, 'logo' => $this->clinicLogo, 'clinic_logo' => $this->clinicLogo, 'images_clinic' => $this->imagesClinic ?? [], diff --git a/src/Clinic/Repository/ClinicRepository.php b/src/Clinic/Repository/ClinicRepository.php index caead174..b0b75aba 100644 --- a/src/Clinic/Repository/ClinicRepository.php +++ b/src/Clinic/Repository/ClinicRepository.php @@ -101,7 +101,7 @@ class ClinicRepository extends ServiceEntityRepository } $rows = $this->getEntityManager()->createQueryBuilder() - ->select('addr.clinicId AS clinic_id', 'cityCat.name AS city', 'provinceCat.name AS state') + ->select('addr.clinicId AS clinic_id', 'cityCat.name AS city', 'provinceCat.name AS state', 'addr.telephone AS telephone') ->from(DoctorAddress::class, 'addr') ->leftJoin('addr.city', 'cityCat') ->leftJoin('addr.province', 'provinceCat') @@ -114,7 +114,7 @@ class ClinicRepository extends ServiceEntityRepository foreach ($rows as $row) { $id = (int) $row['clinic_id']; if (!isset($map[$id])) { - $map[$id] = ['city' => $row['city'], 'state' => $row['state']]; + $map[$id] = ['city' => $row['city'], 'state' => $row['state'], 'telephone' => $row['telephone']]; } } diff --git a/tests/Clinic/ClinicContactSourceTest.php b/tests/Clinic/ClinicContactSourceTest.php new file mode 100644 index 00000000..c9ea33ad --- /dev/null +++ b/tests/Clinic/ClinicContactSourceTest.php @@ -0,0 +1,95 @@ +createClinic('یزد، خیابان قدیمی', '03531234567'); + $this->createAddressFor($clinic, 'کرج، بلوار طالقانی', '02634567890'); + + $data = $this->fetchClinic($clinic); + + $this->assertSame('کرج، بلوار طالقانی', $data['location']); + $this->assertSame('02634567890', $data['phone']); + $this->assertSame('02634567890', $data['phone_number']); + $this->assertSame('کرج', $data['city'][0]['name'] ?? null); + $this->assertSame('البرز', $data['state'][0]['name'] ?? null); + } + + public function testLegacyColumnsAreUsedWhenTheClinicHasNoAddressRecord(): void + { + $clinic = $this->createClinic('یزد، خیابان قدیمی', '03531234567'); + + $data = $this->fetchClinic($clinic); + + $this->assertSame('یزد، خیابان قدیمی', $data['location']); + $this->assertSame('03531234567', $data['phone']); + $this->assertSame([], $data['city']); + } + + public function testBlankAddressFieldsFallBackInsteadOfBlankingTheContactBlock(): void + { + $clinic = $this->createClinic('یزد، خیابان قدیمی', '03531234567'); + $this->createAddressFor($clinic, ' ', null); + + $data = $this->fetchClinic($clinic); + + $this->assertSame('یزد، خیابان قدیمی', $data['location']); + $this->assertSame('03531234567', $data['phone']); + $this->assertSame('کرج', $data['city'][0]['name'] ?? null); + } + + private function createClinic(?string $legacyAddress, ?string $legacyPhone): Clinic + { + $clinic = new Clinic($this->createUser(['ROLE_CLINIC'])); + $clinic->setName('کلینیک تست تماس'); + $clinic->setAddress($legacyAddress); + $clinic->setTelephone($legacyPhone); + $this->em->persist($clinic); + $this->em->flush(); + + return $clinic; + } + + private function createAddressFor(Clinic $clinic, ?string $street, ?string $phone): DoctorAddress + { + $province = new Province('البرز'); + $city = new City('کرج', $province); + $this->em->persist($province); + $this->em->persist($city); + + $address = DoctorAddress::forClinic((int) $clinic->getId()); + $address->setAddress($street); + $address->setTelephone($phone); + $address->setCity($city); + $address->setProvince($province); + $this->em->persist($address); + $this->em->flush(); + + return $address; + } + + private function fetchClinic(Clinic $clinic): array + { + $this->client->request('GET', '/api/v1/clinic/' . $clinic->getUuid()); + $this->assertSame(200, $this->responseCode()); + + return json_decode($this->client->getResponse()->getContent(), true)['data']['data']; + } +}