From b328b36e147ce056ce5e96e90786f2e10f76e949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Mummert?= Date: Sun, 22 Feb 2026 09:29:27 +0100 Subject: [PATCH] Refactor teaser listener to batch prefetch --- .../EventTeaserDataAttributesListener.php | 146 ++++++++++++++---- 1 file changed, 114 insertions(+), 32 deletions(-) diff --git a/src/EventListener/EventTeaserDataAttributesListener.php b/src/EventListener/EventTeaserDataAttributesListener.php index 800cd7c..16ef091 100644 --- a/src/EventListener/EventTeaserDataAttributesListener.php +++ b/src/EventListener/EventTeaserDataAttributesListener.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace MummertMedia\EventManagerBundle\EventListener; -use Contao\CalendarEventsModel; use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\Module; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\ParameterType; @@ -21,6 +21,11 @@ class EventTeaserDataAttributesListener public function onGetAllEvents(array $events, array $calendars, int $start, int $end, Module $module): array { + $eventIds = $this->collectEventIds($events); + $tagMap = $this->fetchTagMap($eventIds); + $organizationMap = $this->fetchOrganizationMap($eventIds); + $locationMap = $this->fetchLocationMap($eventIds); + foreach ($events as $dayKey => $eventsPerDay) { foreach ($eventsPerDay as $timeKey => $eventsPerTime) { foreach ($eventsPerTime as $index => $event) { @@ -37,12 +42,11 @@ class EventTeaserDataAttributesListener if (isset($event['location_id']) && '' !== (string) $event['location_id']) { $events[$dayKey][$timeKey][$index]['location_id'] = (int) $event['location_id']; } else { - $eventModel = CalendarEventsModel::findById($eventId); - $events[$dayKey][$timeKey][$index]['location_id'] = $eventModel ? (int) $eventModel->location_id : null; + $events[$dayKey][$timeKey][$index]['location_id'] = $locationMap[$eventId] ?? null; } - $events[$dayKey][$timeKey][$index]['event_tags'] = implode(',', $this->fetchTagIds($eventId)); - $events[$dayKey][$timeKey][$index]['event_org'] = implode(',', $this->fetchOrganizationIds($eventId)); + $events[$dayKey][$timeKey][$index]['event_tags'] = implode(',', $tagMap[$eventId] ?? []); + $events[$dayKey][$timeKey][$index]['event_org'] = implode(',', $organizationMap[$eventId] ?? []); } } } @@ -51,32 +55,66 @@ class EventTeaserDataAttributesListener } /** @return list */ - private function fetchTagIds(int $eventId): array + private function collectEventIds(array $events): array { - if ($eventId <= 0) { + $eventIds = []; + + foreach ($events as $eventsPerDay) { + foreach ($eventsPerDay as $eventsPerTime) { + foreach ($eventsPerTime as $event) { + if (!isset($event['id'])) { + continue; + } + + $eventIds[] = (int) $event['id']; + } + } + } + + return array_values(array_unique(array_map('intval', $eventIds))); + } + + /** @param list $eventIds + * @return array> + */ + private function fetchTagMap(array $eventIds): array + { + if ([] === $eventIds) { return []; } - $ids = $this->connection->createQueryBuilder() - ->select('tag_id') - ->from('tl_tags_rel') - ->where('ptable = :ptable') - ->andWhere('field = :field') - ->andWhere('pid = :pid') - ->setParameter('ptable', 'tl_calendar_events') - ->setParameter('field', 'tags') - ->setParameter('pid', $eventId, ParameterType::INTEGER) - ->orderBy('tag_id', 'ASC') - ->executeQuery() - ->fetchFirstColumn(); + $rows = $this->connection->executeQuery( + 'SELECT pid, tag_id FROM tl_tags_rel WHERE ptable = ? AND field = ? AND pid IN (?) ORDER BY pid ASC, tag_id ASC', + ['tl_calendar_events', 'tags', $eventIds], + [ParameterType::STRING, ParameterType::STRING, ArrayParameterType::INTEGER], + )->fetchAllAssociative(); - return array_values(array_unique(array_map('intval', $ids))); + $map = []; + + foreach ($rows as $row) { + $eventId = (int) ($row['pid'] ?? 0); + $tagId = (int) ($row['tag_id'] ?? 0); + + if ($eventId <= 0 || $tagId <= 0) { + continue; + } + + $map[$eventId][] = $tagId; + } + + foreach ($map as $eventId => $tagIds) { + $map[$eventId] = array_values(array_unique(array_map('intval', $tagIds))); + } + + return $map; } - /** @return list */ - private function fetchOrganizationIds(int $eventId): array + /** @param list $eventIds + * @return array> + */ + private function fetchOrganizationMap(array $eventIds): array { - if ($eventId <= 0) { + if ([] === $eventIds) { return []; } @@ -84,16 +122,30 @@ class EventTeaserDataAttributesListener foreach ($tables as $table) { try { - $ids = $this->connection->createQueryBuilder() - ->select('organization_id') - ->from($table) - ->where('event_id = :eventId') - ->setParameter('eventId', $eventId, ParameterType::INTEGER) - ->orderBy('organization_id', 'ASC') - ->executeQuery() - ->fetchFirstColumn(); + $rows = $this->connection->executeQuery( + sprintf('SELECT event_id, organization_id FROM %s WHERE event_id IN (?) ORDER BY event_id ASC, organization_id ASC', $table), + [$eventIds], + [ArrayParameterType::INTEGER], + )->fetchAllAssociative(); - return array_values(array_unique(array_map('intval', $ids))); + $map = []; + + foreach ($rows as $row) { + $eventId = (int) ($row['event_id'] ?? 0); + $organizationId = (int) ($row['organization_id'] ?? 0); + + if ($eventId <= 0 || $organizationId <= 0) { + continue; + } + + $map[$eventId][] = $organizationId; + } + + foreach ($map as $eventId => $organizationIds) { + $map[$eventId] = array_values(array_unique(array_map('intval', $organizationIds))); + } + + return $map; } catch (Exception) { continue; } @@ -101,4 +153,34 @@ class EventTeaserDataAttributesListener return []; } + + /** @param list $eventIds + * @return array + */ + private function fetchLocationMap(array $eventIds): array + { + if ([] === $eventIds) { + return []; + } + + $rows = $this->connection->executeQuery( + 'SELECT id, location_id FROM tl_calendar_events WHERE id IN (?)', + [$eventIds], + [ArrayParameterType::INTEGER], + )->fetchAllAssociative(); + + $map = []; + + foreach ($rows as $row) { + $eventId = (int) ($row['id'] ?? 0); + + if ($eventId <= 0) { + continue; + } + + $map[$eventId] = null !== ($row['location_id'] ?? null) ? (int) $row['location_id'] : null; + } + + return $map; + } }