diff --git a/app/Repositories/Summit/DoctrineSummitEventRepository.php b/app/Repositories/Summit/DoctrineSummitEventRepository.php index 8d285b37d..a081a7d19 100644 --- a/app/Repositories/Summit/DoctrineSummitEventRepository.php +++ b/app/Repositories/Summit/DoctrineSummitEventRepository.php @@ -738,7 +738,9 @@ public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Ord if ($event instanceof SummitEvent) { $byId[$event->getId()] = $event; - } else { + } elseif ($row !== null && !($row instanceof PresentationType)) { + // null and PresentationType are the extra elements the query's own WITH joins + // (p, et2) make Doctrine return; any other element is unexpected. Log::warning('DoctrineSummitEventRepository::getAllByPage unexpected hydration row', [ 'type' => is_object($row) ? get_class($row) : gettype($row), 'keys' => is_array($row) ? array_keys($row) : null, diff --git a/tests/Repositories/EventsHydrationRowWarningTest.php b/tests/Repositories/EventsHydrationRowWarningTest.php new file mode 100644 index 000000000..ebf84fef3 --- /dev/null +++ b/tests/Repositories/EventsHydrationRowWarningTest.php @@ -0,0 +1,107 @@ +current_group = IGroup::TrackChairs; + parent::setUp(); + self::insertSummitTestData(); + } + + public function tearDown(): void + { + self::clearSummitTestData(); + parent::tearDown(); + } + + public function testGetAllByPageDoesNotLogUnexpectedHydrationRows(): void + { + $expectedIds = []; + $hasPresentation = false; + $hasPlainEvent = false; + foreach (self::$presentations as $event) { + $expectedIds[] = $event->getId(); + if ($event instanceof Presentation) $hasPresentation = true; + elseif ($event instanceof SummitEvent) $hasPlainEvent = true; + } + sort($expectedIds); + $this->assertTrue( + $hasPresentation && $hasPlainEvent, + 'precondition: fixture must hold presentations and events that are not presentations' + ); + + $summitId = self::$summit->getId(); + + // Evict everything so getAllByPage hydrates the page fresh, as a real request does. + self::$em->clear(); + + $handler = new TestHandler(); + $logger = Log::getLogger(); + $logger->pushHandler($handler); + + try { + /** @var ISummitEventRepository $repo */ + $repo = App::make(ISummitEventRepository::class); + $filter = FilterParser::parse(['summit_id==' . $summitId], ['summit_id' => ['==']]); + $order = new Order([OrderElement::buildAscFor('id')]); + + $response = $repo->getAllByPage(new PagingInfo(1, 200), $filter, $order); + } finally { + $logger->popHandler(); + } + + $returnedIds = array_map(fn($event) => $event->getId(), $response->getItems()); + $this->assertSame($expectedIds, $returnedIds, 'every fixture event must be returned in page order'); + + $hydrationWarnings = array_filter( + $handler->getRecords(), + fn($record) => str_contains((string)($record['message'] ?? $record->message ?? ''), 'unexpected hydration row') + ); + $this->assertCount( + 0, + $hydrationWarnings, + sprintf('getAllByPage must not log "unexpected hydration row"; got %d', count($hydrationWarnings)) + ); + } +}