Refactor teaser listener to batch prefetch

This commit is contained in:
Jürgen Mummert
2026-02-22 09:29:27 +01:00
parent 717d243cec
commit b328b36e14
@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace MummertMedia\EventManagerBundle\EventListener; namespace MummertMedia\EventManagerBundle\EventListener;
use Contao\CalendarEventsModel;
use Contao\CoreBundle\DependencyInjection\Attribute\AsHook; use Contao\CoreBundle\DependencyInjection\Attribute\AsHook;
use Contao\Module; use Contao\Module;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception;
use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\ParameterType;
@@ -21,6 +21,11 @@ class EventTeaserDataAttributesListener
public function onGetAllEvents(array $events, array $calendars, int $start, int $end, Module $module): array 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 ($events as $dayKey => $eventsPerDay) {
foreach ($eventsPerDay as $timeKey => $eventsPerTime) { foreach ($eventsPerDay as $timeKey => $eventsPerTime) {
foreach ($eventsPerTime as $index => $event) { foreach ($eventsPerTime as $index => $event) {
@@ -37,12 +42,11 @@ class EventTeaserDataAttributesListener
if (isset($event['location_id']) && '' !== (string) $event['location_id']) { if (isset($event['location_id']) && '' !== (string) $event['location_id']) {
$events[$dayKey][$timeKey][$index]['location_id'] = (int) $event['location_id']; $events[$dayKey][$timeKey][$index]['location_id'] = (int) $event['location_id'];
} else { } else {
$eventModel = CalendarEventsModel::findById($eventId); $events[$dayKey][$timeKey][$index]['location_id'] = $locationMap[$eventId] ?? null;
$events[$dayKey][$timeKey][$index]['location_id'] = $eventModel ? (int) $eventModel->location_id : null;
} }
$events[$dayKey][$timeKey][$index]['event_tags'] = implode(',', $this->fetchTagIds($eventId)); $events[$dayKey][$timeKey][$index]['event_tags'] = implode(',', $tagMap[$eventId] ?? []);
$events[$dayKey][$timeKey][$index]['event_org'] = implode(',', $this->fetchOrganizationIds($eventId)); $events[$dayKey][$timeKey][$index]['event_org'] = implode(',', $organizationMap[$eventId] ?? []);
} }
} }
} }
@@ -51,32 +55,66 @@ class EventTeaserDataAttributesListener
} }
/** @return list<int> */ /** @return list<int> */
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<int> $eventIds
* @return array<int, list<int>>
*/
private function fetchTagMap(array $eventIds): array
{
if ([] === $eventIds) {
return []; return [];
} }
$ids = $this->connection->createQueryBuilder() $rows = $this->connection->executeQuery(
->select('tag_id') 'SELECT pid, tag_id FROM tl_tags_rel WHERE ptable = ? AND field = ? AND pid IN (?) ORDER BY pid ASC, tag_id ASC',
->from('tl_tags_rel') ['tl_calendar_events', 'tags', $eventIds],
->where('ptable = :ptable') [ParameterType::STRING, ParameterType::STRING, ArrayParameterType::INTEGER],
->andWhere('field = :field') )->fetchAllAssociative();
->andWhere('pid = :pid')
->setParameter('ptable', 'tl_calendar_events')
->setParameter('field', 'tags')
->setParameter('pid', $eventId, ParameterType::INTEGER)
->orderBy('tag_id', 'ASC')
->executeQuery()
->fetchFirstColumn();
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<int> */ /** @param list<int> $eventIds
private function fetchOrganizationIds(int $eventId): array * @return array<int, list<int>>
*/
private function fetchOrganizationMap(array $eventIds): array
{ {
if ($eventId <= 0) { if ([] === $eventIds) {
return []; return [];
} }
@@ -84,16 +122,30 @@ class EventTeaserDataAttributesListener
foreach ($tables as $table) { foreach ($tables as $table) {
try { try {
$ids = $this->connection->createQueryBuilder() $rows = $this->connection->executeQuery(
->select('organization_id') sprintf('SELECT event_id, organization_id FROM %s WHERE event_id IN (?) ORDER BY event_id ASC, organization_id ASC', $table),
->from($table) [$eventIds],
->where('event_id = :eventId') [ArrayParameterType::INTEGER],
->setParameter('eventId', $eventId, ParameterType::INTEGER) )->fetchAllAssociative();
->orderBy('organization_id', 'ASC')
->executeQuery()
->fetchFirstColumn();
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) { } catch (Exception) {
continue; continue;
} }
@@ -101,4 +153,34 @@ class EventTeaserDataAttributesListener
return []; return [];
} }
/** @param list<int> $eventIds
* @return array<int, int|null>
*/
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;
}
} }