[]]], requestBody: new OA\RequestBody( required: true, content: new OA\JsonContent( properties: [ new OA\Property(property: 'name', type: 'string'), new OA\Property(property: 'info', type: 'string', nullable: true), new OA\Property(property: 'address', type: 'string', nullable: true), new OA\Property(property: 'telephone', type: 'string', nullable: true), new OA\Property(property: 'working_days', type: 'array', items: new OA\Items(type: 'string'), nullable: true), new OA\Property(property: '24_7', type: 'boolean', nullable: true), new OA\Property(property: 'latitude', type: 'number', format: 'float', nullable: true), new OA\Property(property: 'longitude', type: 'number', format: 'float', nullable: true), new OA\Property(property: 'state', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'city', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'image_clinic', type: 'array', items: new OA\Items(type: 'string'), nullable: true), new OA\Property(property: 'clinic_logo', type: 'string', nullable: true), new OA\Property(property: 'doctors', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'specialties', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'doctor_services', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'insurance', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), ] ) ), responses: [ new OA\Response( response: 201, description: 'Clinic created', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'data', type: 'object'), ] ) ), new OA\Response(response: 401, description: 'Unauthorized'), ] )] #[Route('/api/v1/clinic', methods: ['POST'])] #[IsGranted('IS_AUTHENTICATED_FULLY')] public function create(Request $request, #[CurrentUser] User $user): JsonResponse { $data = json_decode($request->getContent(), true) ?? []; if (($err = $this->validateGallerySize($data)) !== null) { return $err; } $clinic = new Clinic($user); $this->hydrateClinic($clinic, $data); $this->clinicRepo->save($clinic); // Grant ROLE_CLINIC to user $roles = $user->getRoles(); if (!in_array('ROLE_CLINIC', $roles, true)) { $roles[] = 'ROLE_CLINIC'; $user->setRoles(array_values(array_unique($roles))); $this->userRepo->save($user); } return $this->success(['data' => $clinic->toDetailArray()], 201); } #[OA\Get( path: '/api/v1/clinic/{uuid}', summary: 'Get clinic details by UUID', parameters: [ new OA\Parameter( name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string', format: 'uuid') ), ], responses: [ new OA\Response( response: 200, description: 'Clinic details', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'data', type: 'object'), ] ) ), new OA\Response(response: 404, description: 'Clinic not found'), ] )] #[Route('/api/v1/clinic/{uuid}', methods: ['GET'])] public function show(string $uuid): JsonResponse { $clinic = $this->clinicRepo->findByUuid($uuid); if ($clinic === null) { return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404); } [$stateData, $cityData, $map] = $this->loadLocationData($clinic); return $this->success(['data' => $clinic->toDetailArray($stateData, $cityData, $map)]); } #[OA\Patch( path: '/api/v1/clinic/{uuid}', summary: 'Update clinic details', security: [['bearerAuth' => []]], requestBody: new OA\RequestBody( required: true, content: new OA\JsonContent( properties: [ new OA\Property(property: 'name', type: 'string', nullable: true), new OA\Property(property: 'info', type: 'string', nullable: true), new OA\Property(property: 'address', type: 'string', nullable: true), new OA\Property(property: 'telephone', type: 'string', nullable: true), new OA\Property(property: 'working_days', type: 'array', items: new OA\Items(type: 'string'), nullable: true), new OA\Property(property: '24_7', type: 'boolean', nullable: true), new OA\Property(property: 'latitude', type: 'number', format: 'float', nullable: true), new OA\Property(property: 'longitude', type: 'number', format: 'float', nullable: true), new OA\Property(property: 'state', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'city', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'image_clinic', type: 'array', items: new OA\Items(type: 'string'), nullable: true), new OA\Property(property: 'clinic_logo', type: 'string', nullable: true), new OA\Property(property: 'doctors', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'specialties', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'doctor_services', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), new OA\Property(property: 'insurance', type: 'array', items: new OA\Items(type: 'integer'), nullable: true), ] ) ), parameters: [ new OA\Parameter( name: 'uuid', in: 'path', required: true, schema: new OA\Schema(type: 'string', format: 'uuid') ), ], responses: [ new OA\Response( response: 200, description: 'Clinic updated', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'data', type: 'object'), ] ) ), new OA\Response(response: 401, description: 'Unauthorized'), new OA\Response(response: 403, description: 'Forbidden'), new OA\Response(response: 404, description: 'Clinic not found'), ] )] #[Route('/api/v1/clinic/{uuid}', methods: ['PATCH'])] #[IsGranted('IS_AUTHENTICATED_FULLY')] public function update(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse { $clinic = $this->clinicRepo->findByUuid($uuid); 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) ?? []; if (($err = $this->validateGallerySize($data)) !== null) { return $err; } $this->hydrateClinic($clinic, $data); $this->clinicRepo->save($clinic); [$stateData, $cityData, $map] = $this->loadLocationData($clinic); return $this->success(['data' => $clinic->toDetailArray($stateData, $cityData, $map)]); } #[OA\Get( path: '/api/v1/clinics', summary: 'List clinics with optional filters (paginated)', parameters: [ new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)), new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 20)), new OA\Parameter(name: 'name', in: 'query', required: false, schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'city', in: 'query', required: false, schema: new OA\Schema(type: 'integer')), ], responses: [ new OA\Response( response: 200, description: 'Paginated list of clinics', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'data', type: 'array', items: new OA\Items(type: 'object')), new OA\Property( property: 'meta', properties: [ new OA\Property(property: 'totalRecords', type: 'integer'), new OA\Property(property: 'totalPages', type: 'integer'), new OA\Property(property: 'currentPage', type: 'integer'), ], type: 'object' ), ] ) ), ] )] #[Route('/api/v1/clinics', methods: ['GET'])] public function list(Request $request): JsonResponse { $filters = $this->domainResolver->applyToListFilters($request->query->all()); $result = $this->clinicRepo->findWithFilters($filters); $clinicIds = array_map(fn(Clinic $c) => $c->getId(), $result['items']); $locations = $this->clinicRepo->findAddressLocations($clinicIds); 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']); }, $result['items']), $result['total'], $result['page'], $result['limit'] ); } #[OA\Get( path: '/api/v1/clinic/doctor-list/{clinicUuid}', summary: 'Get list of doctors for a clinic', parameters: [ new OA\Parameter( name: 'clinicUuid', in: 'path', required: true, schema: new OA\Schema(type: 'string', format: 'uuid') ), ], responses: [ new OA\Response( response: 200, description: 'List of doctors', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property( property: 'data', properties: [ new OA\Property(property: 'data', type: 'array', items: new OA\Items(type: 'object')), ], type: 'object' ), ] ) ), new OA\Response(response: 404, description: 'Clinic not found'), ] )] #[Route('/api/v1/clinic/doctor-list/{clinicUuid}', methods: ['GET'])] public function doctorList(string $clinicUuid, Request $request): JsonResponse { $clinic = $this->clinicRepo->findByUuid($clinicUuid); if ($clinic === null) { return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404); } $result = $this->doctorRepo->findByClinicWithFilters((int) $clinic->getId(), $request->query->all()); $clinicDoctors = $result['items']; $scheduleMap = []; foreach ($this->scheduleRepo->findByDoctors($clinicDoctors) as $schedule) { $scheduleMap[$schedule->getDoctor()->getId()] = $schedule; } $doctors = array_map( fn(Doctor $d) => $d->toListArray($scheduleMap[$d->getId()] ?? null), $clinicDoctors ); return $this->success([ 'data' => $doctors, 'meta' => [ 'totalRecords' => $result['total'], 'totalPages' => $result['totalPages'], 'currentPage' => $result['page'], ], ]); } #[OA\Delete( path: '/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}', summary: 'Detach a doctor from a clinic (admin only)', security: [['bearerAuth' => []]], parameters: [ new OA\Parameter(name: 'clinicUuid', in: 'path', required: true, schema: new OA\Schema(type: 'string', format: 'uuid')), new OA\Parameter(name: 'doctorUuid', in: 'path', required: true, schema: new OA\Schema(type: 'string', format: 'uuid')), ], responses: [ new OA\Response(response: 200, description: 'Doctor detached from clinic'), new OA\Response(response: 404, description: 'Clinic or doctor not found'), ] )] #[Route('/api/v1/admin/clinic/{clinicUuid}/doctor/{doctorUuid}', methods: ['DELETE'])] #[IsGranted('ROLE_ADMIN')] public function detachDoctor(string $clinicUuid, string $doctorUuid): JsonResponse { $clinic = $this->clinicRepo->findByUuid($clinicUuid); if ($clinic === null) { return $this->error(ErrorCodes::ERR_VALIDATION_002, 'کلینیک یافت نشد', 404); } $doctor = $this->doctorRepo->findByUuid($doctorUuid); if ($doctor === null) { return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'پزشک یافت نشد', 404); } if (!$clinic->hasDoctor($doctor)) { return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'این پزشک به کلینیک متصل نیست', 404); } $clinic->removeDoctor($doctor); $this->clinicRepo->save($clinic); return $this->success(['message' => 'پزشک از کلینیک جدا شد']); } #[OA\Post( path: '/file/upload/clinic_pro/clinic/field_image_clinic', summary: 'Upload a clinic gallery image', security: [['bearerAuth' => []]], requestBody: new OA\RequestBody( required: true, content: new OA\MediaType( mediaType: 'application/octet-stream', schema: new OA\Schema(type: 'string', format: 'binary') ) ), responses: [ new OA\Response( response: 200, description: 'Image uploaded', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property( property: 'data', properties: [ new OA\Property(property: 'fid', type: 'integer'), new OA\Property(property: 'uuid', type: 'string', format: 'uuid'), new OA\Property(property: 'url', type: 'string'), new OA\Property(property: 'filename', type: 'string'), new OA\Property(property: 'filemime', type: 'string'), new OA\Property(property: 'filesize', type: 'integer'), ], type: 'object' ), ] ) ), new OA\Response(response: 401, description: 'Unauthorized'), new OA\Response(response: 422, description: 'Invalid file'), ] )] #[Route('/file/upload/clinic_pro/clinic/field_image_clinic', methods: ['POST'])] #[IsGranted('IS_AUTHENTICATED_FULLY')] public function uploadImage(Request $request): JsonResponse { return $this->handleFileUpload($request, 'clinics/gallery'); } #[OA\Post( path: '/file/upload/clinic_pro/clinic/field_clinic_logo', summary: 'Upload a clinic logo image', security: [['bearerAuth' => []]], requestBody: new OA\RequestBody( required: true, content: new OA\MediaType( mediaType: 'application/octet-stream', schema: new OA\Schema(type: 'string', format: 'binary') ) ), responses: [ new OA\Response( response: 200, description: 'Logo uploaded', content: new OA\JsonContent( properties: [ new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property( property: 'data', properties: [ new OA\Property(property: 'fid', type: 'integer'), new OA\Property(property: 'uuid', type: 'string', format: 'uuid'), new OA\Property(property: 'url', type: 'string'), new OA\Property(property: 'filename', type: 'string'), new OA\Property(property: 'filemime', type: 'string'), new OA\Property(property: 'filesize', type: 'integer'), ], type: 'object' ), ] ) ), new OA\Response(response: 401, description: 'Unauthorized'), new OA\Response(response: 422, description: 'Invalid file'), ] )] #[Route('/file/upload/clinic_pro/clinic/field_clinic_logo', methods: ['POST'])] #[IsGranted('IS_AUTHENTICATED_FULLY')] public function uploadLogo(Request $request): JsonResponse { return $this->handleFileUpload($request, 'clinics/logo'); } // ── Helpers ─────────────────────────────────────────────────────────────── private function validateGallerySize(array $data): ?JsonResponse { if (array_key_exists('image_clinic', $data) && is_array($data['image_clinic']) && count($data['image_clinic']) > self::MAX_GALLERY_IMAGES ) { return $this->error( ErrorCodes::ERR_VALIDATION_001, 'گالری تصاویر حداکثر ' . self::MAX_GALLERY_IMAGES . ' عکس می‌تواند داشته باشد', 422, 'image_clinic', ); } return null; } private function hydrateClinic(Clinic $clinic, array $data): void { if (array_key_exists('name', $data)) $clinic->setName($data['name']); if (array_key_exists('info', $data)) $clinic->setInfo($data['info']); if (array_key_exists('address', $data)) $clinic->setAddress($data['address']); if (array_key_exists('telephone', $data)) $clinic->setTelephone($data['telephone']); if (array_key_exists('working_days', $data)) $clinic->setWorkingDays($data['working_days']); if (array_key_exists('24_7', $data)) $clinic->setIs247((bool) $data['24_7']); if (array_key_exists('latitude', $data)) $clinic->setLatitude((float) $data['latitude']); if (array_key_exists('longitude', $data)) $clinic->setLongitude((float) $data['longitude']); // Location if (!empty($data['state']) && is_array($data['state'])) { $clinic->setProvinceId((int) $data['state'][0]); } if (!empty($data['city']) && is_array($data['city'])) { $clinic->setCityId((int) $data['city'][0]); } if (array_key_exists('social_media', $data) && is_array($data['social_media'])) { $allowedKeys = ['instagram', 'telegram', 'aparat', 'youtube', 'linkedin']; $socialMedia = []; foreach ($allowedKeys as $key) { $value = $data['social_media'][$key] ?? null; $socialMedia[$key] = (is_string($value) && filter_var($value, FILTER_VALIDATE_URL)) ? $value : null; } $clinic->setSocialMedia($socialMedia); } // Images stored as JSON (from upload response); gallery is capped at 5. if (array_key_exists('image_clinic', $data) && is_array($data['image_clinic'])) { $clinic->setImagesClinic(array_slice(array_values($data['image_clinic']), 0, self::MAX_GALLERY_IMAGES)); } if (array_key_exists('clinic_logo', $data)) { $logo = $data['clinic_logo']; // Accept either a plain URL string or an upload-response object with 'url' key if (is_array($logo) && isset($logo['url'])) { $logo = $logo['url']; } elseif (is_array($logo) && !empty($logo[0]['url'])) { $logo = $logo[0]['url']; } $clinic->setClinicLogo(is_string($logo) ? $logo : null); } // ManyToMany: doctors if (array_key_exists('doctors', $data) && is_array($data['doctors'])) { $clinic->getDoctors()->clear(); foreach ($data['doctors'] as $doctorId) { $doctor = $this->doctorRepo->find((int) $doctorId); if ($doctor !== null) { $clinic->getDoctors()->add($doctor); } } } // ManyToMany: specialties if (array_key_exists('specialties', $data) && is_array($data['specialties'])) { $clinic->getSpecialties()->clear(); foreach ($data['specialties'] as $id) { $specialty = $this->specialtyRepo->find((int) $id); if ($specialty !== null) { $clinic->getSpecialties()->add($specialty); } } } // ManyToMany: services (doctor_services) if (array_key_exists('doctor_services', $data) && is_array($data['doctor_services'])) { $clinic->getServices()->clear(); foreach ($data['doctor_services'] as $id) { $service = $this->serviceRepo->find((int) $id); if ($service !== null) { $clinic->getServices()->add($service); } } } // ManyToMany: insurances if (array_key_exists('insurance', $data) && is_array($data['insurance'])) { $clinic->getInsurances()->clear(); foreach ($data['insurance'] as $id) { $insurance = $this->insuranceRepo->find((int) $id); if ($insurance !== null) { $clinic->getInsurances()->add($insurance); } } } } private function loadLocationData(Clinic $clinic): array { $provinceData = []; $cityData = []; $map = ['latitude' => null, 'longitude' => null]; // City/province/coordinates live on the clinic's address (DoctorAddress // linked by clinicId), not on the deprecated columns of the clinic itself. $address = $this->addressRepo->findOneByClinic((int) $clinic->getId()); if ($address !== null) { $province = $address->getProvince(); if ($province !== null) { $provinceData = ['uuid' => $province->getUuid(), 'id' => (string) $province->getId(), 'name' => $province->getName()]; } $city = $address->getCity(); if ($city !== null) { $cityData = [ 'uuid' => $city->getUuid(), 'id' => (string) $city->getId(), 'name' => $city->getName(), 'parent' => $city->getProvince()?->getId() !== null ? (string) $city->getProvince()->getId() : null, ]; } $map = [ 'latitude' => $address->getLatitude() !== null ? (string) $address->getLatitude() : null, 'longitude' => $address->getLongitude() !== null ? (string) $address->getLongitude() : null, ]; } return [$provinceData, $cityData, $map]; } private function handleFileUpload(Request $request, string $subDir): JsonResponse { $content = $request->getContent(); $disposition = $request->headers->get('Content-Disposition', ''); preg_match('/filename=["\']?([^"\';\s]+)["\']?/i', $disposition, $m); $filename = $m[1] ?? 'upload.jpg'; $tmpPath = sys_get_temp_dir() . '/' . uniqid('upload_', true); file_put_contents($tmpPath, $content); try { $safeFilename = $this->fileValidator->sanitizeFilename($filename); $mime = $this->fileValidator->detectMimeType($tmpPath); $year = date('Y'); $month = date('m'); $dir = $this->projectDir . '/public/uploads/' . $subDir . '/' . $year . '-' . $month; if (!is_dir($dir)) mkdir($dir, 0755, true); $storedName = uniqid('', true) . '_' . $safeFilename; rename($tmpPath, $dir . '/' . $storedName); $url = '/uploads/' . $subDir . '/' . $year . '-' . $month . '/' . $storedName; return $this->success([ 'fid' => time(), 'uuid' => Uuid::v4()->toRfc4122(), 'url' => $url, 'filename' => $safeFilename, 'filemime' => $mime, 'filesize' => strlen($content), ]); } catch (\Throwable $e) { if (file_exists($tmpPath)) unlink($tmpPath); 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); } if ($this->addressRepo->countByClinic($clinic->getId()) > 0) { return $this->error(ErrorCodes::ERR_CONFLICT_001, 'این کلینیک قبلاً آدرس دارد. برای ویرایش از endpoint PATCH استفاده کنید', 409); } $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); } $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); } } }