Eager Loading
Eager loading is the primary tool for preventing N+1 query problems in the CPR backend.
The N+1 Problem
php
// BAD: 1 query for patients + N queries for visits (one per patient)
$patients = Patient::all();
foreach ($patients as $patient) {
echo $patient->visits->count(); // Triggers a query each time
}
// GOOD: 2 queries total (patients + visits)
$patients = Patient::with('visits')->get();
foreach ($patients as $patient) {
echo $patient->visits->count(); // Already loaded, no query
}Where Eager Loading is Applied
Repositories
The AbstractFilterableRepository::buildQuery() method is where eager loading happens:
php
// TransactionRepository
protected function buildQuery(?string $search, ?array $filters): Builder
{
return Transaction::query()
->with(['items', 'patient', 'branch', 'paymentMethod'])
->when($search, fn ($q) =>
$q->where('transaction_number', 'like', "%{$search}%")
);
}Controllers (Single Record)
When loading a single record with its relations:
php
public function show(int $id)
{
$transaction = $this->service->find($id);
// Service loads: Transaction::with(['items', 'patient'])->findOrFail($id)
return TransactionResource::make($transaction);
}API Resources (Conditional)
Resources use whenLoaded() to conditionally include relations:
php
class TransactionResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'patient' => PatientResource::make($this->whenLoaded('patient')),
'items' => TransactionItemResource::collection($this->whenLoaded('items')),
];
}
}If the relation wasn't eager-loaded, whenLoaded() simply omits it from the response instead of triggering a lazy load.
Common Eager Loading Patterns
Nested Relations
php
// Load queue tickets with their patient visits and patients
QueueTicket::with('visit.patient')->get();Counting Without Loading
php
// Don't load all users, just count them
Branch::withCount('users')->get();
// Access: $branch->users_countConstraining Eager Loads
php
// Only load active queue tickets
PatientVisit::with(['queueTickets' => function ($query) {
$query->where('status', 'waiting');
}])->get();Guidelines
- Always eager load in repositories when you know the relation will be used
- Use
whenLoaded()in Resources to avoid accidental lazy loading - Use
withCount()when you only need counts, not full records - Load nested relations with dot notation (
visit.patient) - Don't over-eager-load - Only load what the current endpoint needs