perf(appointment): batch-fetch pending payments in expiry loop (fix N+1)
AppointmentExpiryService ran one findPendingByAppointment query per expiring booking. Add PaymentRepository::findPendingByAppointments (one IN query keyed by appointment id) and use it. Test covers expiry + payment cancellation for several appointments at once.
This commit is contained in:
@@ -62,6 +62,36 @@ class PaymentRepository extends ServiceEntityRepository
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch variant of findPendingByAppointment: all pending payments for the
|
||||
* given appointments in ONE query, keyed by appointment id. Avoids the N+1
|
||||
* in AppointmentExpiryService when many bookings expire at once.
|
||||
*
|
||||
* @param Appointment[] $appointments
|
||||
* @return array<int, Payment> appointment id => pending Payment
|
||||
*/
|
||||
public function findPendingByAppointments(array $appointments): array
|
||||
{
|
||||
if ($appointments === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$payments = $this->createQueryBuilder('p')
|
||||
->andWhere('p.appointment IN (:appointments)')
|
||||
->andWhere('p.status = :status')
|
||||
->setParameter('appointments', $appointments)
|
||||
->setParameter('status', Payment::STATUS_PENDING)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
$byAppointment = [];
|
||||
foreach ($payments as $payment) {
|
||||
$byAppointment[$payment->getAppointment()->getId()] = $payment;
|
||||
}
|
||||
|
||||
return $byAppointment;
|
||||
}
|
||||
|
||||
public function save(Payment $entity, bool $flush = true): void
|
||||
{
|
||||
$this->getEntityManager()->persist($entity);
|
||||
|
||||
Reference in New Issue
Block a user