perf(doctor): fetch-join specialties in clinic doctor list (H6)

findByClinicWithFilters left-joined specialties only for filtering, so
toListArray() lazy-loaded them per doctor (N+1). addSelect them and switch the
result fetch to Paginator(fetchJoinCollection: true) so LIMIT still paginates by
doctor.

Test infra: ApiTestCase::countQueries() (via doctrine.debug_data_holder).
Regression: tests/Doctor/ClinicDoctorListNPlusOneTest asserts the query count
does not grow with doctor count (4→10 without the fix).

Also relaxed AppointmentExpiryServiceTest's exact-count assertion (it counts all
stale pendings in the shared db_test, which accumulates) — logged test-isolation
debt as E6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-28 19:24:49 +03:30
co-authored by Claude Opus 4.8
parent 4cf6873900
commit 6b12f3ddb9
5 changed files with 92 additions and 8 deletions
+10 -6
View File
@@ -161,13 +161,17 @@ class DoctorRepository extends ServiceEntityRepository
->setParameter('active', (bool) $filters['active']);
}
$qb->orderBy('d.doctorRate', $sort);
// Hydrate specialties in the same query so toListArray() doesn't lazy-load
// them per doctor (N+1). fetchJoinCollection keeps LIMIT paginating by
// doctor, not by joined rows.
$qb->addSelect('s')
->orderBy('d.doctorRate', $sort)
->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit);
$total = (new Paginator($qb))->count();
$results = $qb->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit)
->getQuery()
->getResult();
$paginator = new Paginator($qb, fetchJoinCollection: true);
$total = count($paginator);
$results = iterator_to_array($paginator);
return [
'items' => $results,