b29a268b1d
- Move completed plan files to .plans/done/ - Move 18 open plan files to .plans/open/ - Update .gitignore to exclude .verified_plans temp file - Verified all 18 open plans still describe unimplemented issues
1.8 KiB
1.8 KiB
Issue #200: N+1 Query Problem in Member List
Problem
The MemberService::findAll() method (and similar methods) loads members, then makes 3 additional queries per member to fetch addresses, phones, and emails:
public function findAll(?int $limit = null, ?int $offset = null): array {
$members = $this->memberMapper->findAll($limit, $offset);
return array_map(function (Member $member) {
$id = $member->getId();
$addresses = $this->addressMapper->findByMemberId($id); // +1 query
$phones = $this->phoneMapper->findByMemberId($id); // +1 query
$emails = $this->emailMapper->findByMemberId($id); // +1 query
return $this->buildMemberResponse($member, $addresses, $phones, $emails);
}, $members);
}
For a page of 20 members, this results in 1 + 20×3 = 61 queries.
Impact
- Slow page loads, especially with pagination
- Increased database load
- Poor performance on larger member lists
Solution
Create a new mapper method that uses JOINs to fetch all data in a single query:
SELECT m.*, a.*, p.*, e.*
FROM mv_members m
LEFT JOIN mv_addresses a ON m.id = a.member_id
LEFT JOIN mv_phones p ON m.id = p.member_id
LEFT JOIN mv_emails e ON m.id = e.member_id
WHERE m.deleted_at IS NULL
ORDER BY m.nachname, m.vorname
Then parse the result set in PHP to group related sub-entities.
Tasks
- Create
MemberMapper::findAllWithRelations(?int $limit, ?int $offset): arraythat returns raw joined data - Create a DTO or use an associative array to represent joined results
- Update
MemberService::findAll()to use the new method - Apply same pattern to
findFiltered(),findByFamily(),search(),fullTextSearch() - Benchmark before/after query counts
Labels
- enhancement
- backend
- priority:high
- performance