11 KiB
رفع فیلدهای free_turn و hours_of_work در API لیست پزشکان
زمینه
endpoint GET /api/v1/doctors و GET /api/v1/doctor/{uuid} در پاسخ خود دو فیلد free_turn و hours_of_work دارند که همیشه مقدار ثابت «نوبت آزادی موجود نیست» و «برنامه کاری تنظیم نشده» برمیگردانند — حتی برای پزشکانی که برنامه هفتگی (weekly_schedules) ست کردهاند. همچنین activeDoctorAppointment (فیلد active) باید با وجود یا نبود برنامه هفتگی هماهنگ باشد.
مشکل / هدف
free_turn: باید نزدیکترین روز کاری پزشک را نشان دهد (مثلاً «شنبه ۹:۰۰–۱۳:۰۰»)؛ اگر برنامهای نداشت «نوبت آزادی موجود نیست»hours_of_work: باید ساعتهای کاری روزهای فعال را خلاصه کند (مثلاً «شنبه تا چهارشنبه ۹–۱۳ و ۱۴–۱۸»)؛ اگر برنامه نداشت «برنامه کاری تنظیم نشده»active(activeDoctorAppointment): اگر پزشک برنامه هفتگی نداشته باشد یا همه روزهاsessions: []باشند، بایدfalseبرگردد — نوبتدهی غیرفعال
فایلهای مرتبط
| فایل | نقش |
|---|---|
src/Doctor/Entity/Doctor.php |
متدهای toListArray() و toDetailArray() — مقادیر hardcoded اینجاست |
src/Doctor/Repository/DoctorRepository.php |
findWithFilters() — JOIN با weekly_schedules ندارد |
src/Doctor/Controller/DoctorController.php |
list() (خط ۲۳۳) و show() — از toListArray/toDetailArray استفاده میکنند |
src/Appointment/Entity/WeeklySchedule.php |
Entity برنامه هفتگی — فیلد setting آرایه ۷ عنصری (شنبه تا جمعه) |
src/Appointment/Repository/WeeklyScheduleRepository.php |
findByDoctor(Doctor $doctor): ?WeeklySchedule موجود است |
وضعیت فعلی
Doctor.php — toListArray() خط ۱۷۸:
public function toListArray(): array
{
return [
// ...
'free_turn' => 'نوبت آزادی موجود نیست', // ❌ hardcoded
'hours_of_work' => 'برنامه کاری تنظیم نشده', // ❌ hardcoded
'active' => $this->activeDoctorAppointment,
];
}
Doctor.php — toDetailArray() خط ۱۹۸:
'free_turn' => 'نوبت آزادی موجود نیست', // ❌ hardcoded
'hours_of_work' => 'برنامه کاری تنظیم نشده', // ❌ hardcoded
ساختار setting در weekly_schedules:
آرایه ۷ عنصری (ایندکس ۰=شنبه، ۱=یکشنبه، ..., ۶=جمعه):
[
{"sessions": [{"active": true, "start_time": "09:00", "end_time": "13:00", ...}, ...]},
{"sessions": [{"active": true, "start_time": "14:00", "end_time": "18:00", ...}]},
...
{"sessions": []} // جمعه — تعطیل
]
DoctorRepository.findWithFilters() — JOIN ندارد:
$qb = $this->createQueryBuilder('d')
->leftJoin('d.specialties', 's')
->leftJoin('d.provinces', 'pr')
->leftJoin('d.cities', 'ci')
// ❌ هیچ JOIN با weekly_schedules ندارد
وظایف
۱. اضافه کردن متد کمکی به Doctor entity
در src/Doctor/Entity/Doctor.php یک متد computeScheduleFields(?WeeklySchedule $schedule): array اضافه کن:
use App\Appointment\Entity\WeeklySchedule;
private const DAY_NAMES = ['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'];
public function computeScheduleFields(?WeeklySchedule $schedule): array
{
if ($schedule === null) {
return [
'free_turn' => 'نوبت آزادی موجود نیست',
'hours_of_work' => 'برنامه کاری تنظیم نشده',
'has_schedule' => false,
];
}
$setting = $schedule->getSetting(); // آرایه ۷ عنصری
// محاسبه hours_of_work — ساعتهای روزهای فعال
$workDays = [];
foreach ($setting as $dayIdx => $day) {
$activeSessions = array_filter(
$day['sessions'] ?? [],
fn($s) => ($s['active'] ?? false) && !empty($s['start_time'])
);
if (!empty($activeSessions)) {
$times = array_map(fn($s) => $s['start_time'] . '–' . $s['end_time'], $activeSessions);
$workDays[] = self::DAY_NAMES[$dayIdx] . ': ' . implode(' و ', $times);
}
}
if (empty($workDays)) {
return [
'free_turn' => 'نوبت آزادی موجود نیست',
'hours_of_work' => 'برنامه کاری تنظیم نشده',
'has_schedule' => false,
];
}
// محاسبه free_turn — نزدیکترین روز کاری فعال
// روز هفته فعلی را بگیر (PHP: 0=یکشنبه...6=شنبه → تبدیل به ایندکس ایرانی)
$phpDay = (int) date('w'); // 0=Sun, 6=Sat
$iranDay = $phpDay === 0 ? 1 : ($phpDay === 6 ? 0 : $phpDay + 1); // 0=Sat, 1=Sun, ...
$freeTurn = null;
for ($i = 0; $i < 7; $i++) {
$idx = ($iranDay + $i) % 7;
$activeSessions = array_filter(
$setting[$idx]['sessions'] ?? [],
fn($s) => ($s['active'] ?? false) && !empty($s['start_time'])
);
if (!empty($activeSessions)) {
$first = array_values($activeSessions)[0];
$freeTurn = self::DAY_NAMES[$idx] . ' ' . $first['start_time'] . '–' . $first['end_time'];
break;
}
}
return [
'free_turn' => $freeTurn ?? 'نوبت آزادی موجود نیست',
'hours_of_work' => implode(' | ', $workDays),
'has_schedule' => true,
];
}
۲. آپدیت toListArray() و toDetailArray()
toListArray() و toDetailArray() باید یک ?WeeklySchedule دریافت کنند:
public function toListArray(?WeeklySchedule $schedule = null): array
{
$scheduleFields = $this->computeScheduleFields($schedule);
return [
// ... بقیه فیلدها
'free_turn' => $scheduleFields['free_turn'],
'hours_of_work' => $scheduleFields['hours_of_work'],
'active' => $this->activeDoctorAppointment && $scheduleFields['has_schedule'],
];
}
public function toDetailArray(?WeeklySchedule $schedule = null): array
{
$scheduleFields = $this->computeScheduleFields($schedule);
return [
// ... بقیه فیلدها
'free_turn' => $scheduleFields['free_turn'],
'hours_of_work' => $scheduleFields['hours_of_work'],
'active' => $this->activeDoctorAppointment && $scheduleFields['has_schedule'],
// ... address, state, city
];
}
۳. آپدیت DoctorRepository.findWithFilters()
LEFT JOIN با weekly_schedules اضافه کن تا schedule را یکجا load کند:
$qb = $this->createQueryBuilder('d')
->leftJoin('d.specialties', 's')
->leftJoin('d.provinces', 'pr')
->leftJoin('d.cities', 'ci')
->addSelect('d') // ensure d is selected for eager loading
->distinct();
نکته مهم: از eager loading یا جداگانه query استفاده کن. چون Doctor entity OneToOne با WeeklySchedule ندارد (رابطه از طرف WeeklySchedule است)، بهترین راه این است که در Controller بعد از گرفتن لیست doctors، scheduleها را batch load کنی.
۴. آپدیت DoctorController — متد list()
در src/Doctor/Controller/DoctorController.php متد list() خط ۲۳۳:
public function __construct(
// اضافه کن:
private readonly WeeklyScheduleRepository $scheduleRepo,
// ...
)
public function list(Request $request): JsonResponse
{
$filters = $request->query->all();
$result = $this->doctorRepo->findWithFilters($filters);
// batch load schedules برای همه doctors
$scheduleMap = [];
foreach ($this->scheduleRepo->findByDoctors($result['items']) as $schedule) {
$scheduleMap[$schedule->getDoctor()->getId()] = $schedule;
}
return $this->paginated(
array_map(
fn(Doctor $d) => $d->toListArray($scheduleMap[$d->getId()] ?? null),
$result['items']
),
$result['total'],
$result['page'],
$result['limit']
);
}
۵. اضافه کردن findByDoctors() به WeeklyScheduleRepository
/**
* @param Doctor[] $doctors
* @return WeeklySchedule[]
*/
public function findByDoctors(array $doctors): array
{
if (empty($doctors)) return [];
return $this->createQueryBuilder('ws')
->where('ws.doctor IN (:doctors)')
->setParameter('doctors', $doctors)
->getQuery()
->getResult();
}
۶. آپدیت متدهای show() در DoctorController
هر جایی که $doctor->toDetailArray() صدا زده میشود (خطوط ۱۱۷، ۱۶۳، ۱۸۴، ۳۱۲)، schedule را پاس بده:
$schedule = $this->scheduleRepo->findByDoctor($doctor);
return $this->success(['data' => $doctor->toDetailArray($schedule)]);
نکات مهم
- Doctor entity رابطه مستقیم با WeeklySchedule ندارد — رابطه OneToOne از طرف WeeklySchedule به Doctor است؛ پس
$doctor->getSchedule()وجود ندارد و باید از Repository بخوانی - Circular dependency:
Doctor.phpنباید مستقیم WeeklyScheduleRepository inject کند — بهتر است در Controller schedule را بگیری و به متد پاس بدهی (دقیقاً همان الگوی پیشنهادی بالا) - active flag: فقط وقتی هم
activeDoctorAppointment=trueهمhas_schedule=trueباشد نوبتدهی فعال است - batch load: برای endpoint لیست، حتماً از
findByDoctors()batch استفاده کن — N+1 query نساز - ایندکس روزها:
setting[0]=شنبه,setting[1]=یکشنبه, ...,setting[6]=جمعه— PHPdate('w')را باید به این ایندکس تبدیل کنی - sessions خالی: اگر
sessions: []باشد آن روز تعطیل است — فقطactive: trueوstart_timeغیرخالی معتبر است - مستندات: بعد از تغییر،
docs/api/doctor.mdرا آپدیت کن (فیلدهایfree_turn،hours_of_work،activeرا توضیح بده) - هیچ migration لازم نیست — Entity تغییر ساختاری ندارد