From 0685f8eeb19ceac9dbcb9f80a7e07f085f1e5c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Mummert?= Date: Sun, 22 Feb 2026 11:01:12 +0100 Subject: [PATCH] Add event filter frontend module with server-side aggregates --- contao/dca/tl_module.php | 1 + contao/languages/de/modules.php | 1 + contao/languages/en/modules.php | 1 + .../templates/frontend/event_filter.html.twig | 27 +++ .../Frontend/EventFilterController.php | 193 ++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 contao/templates/frontend/event_filter.html.twig create mode 100644 src/Controller/Frontend/EventFilterController.php diff --git a/contao/dca/tl_module.php b/contao/dca/tl_module.php index 66314f4..54ee83a 100644 --- a/contao/dca/tl_module.php +++ b/contao/dca/tl_module.php @@ -7,6 +7,7 @@ use Contao\StringUtil; $GLOBALS['TL_DCA']['tl_module']['palettes']['member_organizations'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; $GLOBALS['TL_DCA']['tl_module']['palettes']['member_events'] = '{title_legend},name,headline,type;{eventmanager_legend},editPage;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; +$GLOBALS['TL_DCA']['tl_module']['palettes']['event_filter'] = '{title_legend},name,headline,type;{eventmanager_legend},cal_calendar;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; $GLOBALS['TL_DCA']['tl_module']['palettes']['organization_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,logoFolder,organizationTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; $GLOBALS['TL_DCA']['tl_module']['palettes']['event_edit'] = '{title_legend},name,headline,type;{eventmanager_legend},listPage,eventFolder,termsPage,frontendAuthorId,frontendArchiveId,eventTypeTags;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID'; diff --git a/contao/languages/de/modules.php b/contao/languages/de/modules.php index 8451e0b..084d473 100644 --- a/contao/languages/de/modules.php +++ b/contao/languages/de/modules.php @@ -9,4 +9,5 @@ $GLOBALS['TL_LANG']['FMD']['eventmanager'] = 'Event-Manager'; $GLOBALS['TL_LANG']['FMD']['member_organizations'] = ['Meine Organisationen', 'Listet Organisationen des eingeloggten Mitglieds auf.']; $GLOBALS['TL_LANG']['FMD']['organization_edit'] = ['Organisation bearbeiten', 'Bearbeitungsformular für eine Organisation.']; $GLOBALS['TL_LANG']['FMD']['member_events'] = ['Meine Veranstaltungen', 'Listet Veranstaltungen der zugeordneten Organisationen auf.']; +$GLOBALS['TL_LANG']['FMD']['event_filter'] = ['Eventfilter', 'Filter für kommende Veranstaltungen (Tags, Orte, Veranstalter).']; $GLOBALS['TL_LANG']['FMD']['event_edit'] = ['Veranstaltung bearbeiten', 'Bearbeitungsformular für eine Veranstaltung.']; diff --git a/contao/languages/en/modules.php b/contao/languages/en/modules.php index 0267ecf..fc33698 100644 --- a/contao/languages/en/modules.php +++ b/contao/languages/en/modules.php @@ -9,4 +9,5 @@ $GLOBALS['TL_LANG']['FMD']['eventmanager'] = 'Event manager'; $GLOBALS['TL_LANG']['FMD']['member_organizations'] = ['My organizations', 'Lists organizations of the logged-in member.']; $GLOBALS['TL_LANG']['FMD']['organization_edit'] = ['Edit organization', 'Edit form for one organization.']; $GLOBALS['TL_LANG']['FMD']['member_events'] = ['My events', 'Lists events of the member organizations.']; +$GLOBALS['TL_LANG']['FMD']['event_filter'] = ['Event filter', 'Filters upcoming events (tags, locations, organizers).']; $GLOBALS['TL_LANG']['FMD']['event_edit'] = ['Edit event', 'Edit form for one event.']; diff --git a/contao/templates/frontend/event_filter.html.twig b/contao/templates/frontend/event_filter.html.twig new file mode 100644 index 0000000..0958379 --- /dev/null +++ b/contao/templates/frontend/event_filter.html.twig @@ -0,0 +1,27 @@ +
+ + + {% for tag in tagButtons|default([]) %} + + {% endfor %} + +
+ + +
+ +
+ + +
+
diff --git a/src/Controller/Frontend/EventFilterController.php b/src/Controller/Frontend/EventFilterController.php new file mode 100644 index 0000000..c0a4eed --- /dev/null +++ b/src/Controller/Frontend/EventFilterController.php @@ -0,0 +1,193 @@ +cal_calendar, true)); + $eventIds = $this->findUpcomingEventIds($calendarIds); + + $template->set('tagButtons', $this->findTagButtons($eventIds)); + $template->set('locations', $this->findLocations($eventIds)); + $template->set('organizations', $this->findOrganizations($eventIds)); + + return $template->getResponse(); + } + + /** + * @param list $calendarIds + * + * @return list + */ + private function findUpcomingEventIds(array $calendarIds): array + { + $today = strtotime('today'); + + if ([] === $calendarIds) { + $rows = $this->connection->executeQuery( + <<<'SQL' + SELECT e.id + FROM tl_calendar_events e + INNER JOIN tl_calendar c ON c.id=e.pid + WHERE e.published='1' + AND c.published='1' + AND (e.startTime>=? OR e.endTime>=?) + ORDER BY e.startTime ASC + SQL, + [$today, $today], + [\PDO::PARAM_INT, \PDO::PARAM_INT], + )->fetchFirstColumn(); + } else { + $rows = $this->connection->executeQuery( + <<<'SQL' + SELECT e.id + FROM tl_calendar_events e + INNER JOIN tl_calendar c ON c.id=e.pid + WHERE e.pid IN (?) + AND e.published='1' + AND c.published='1' + AND (e.startTime>=? OR e.endTime>=?) + ORDER BY e.startTime ASC + SQL, + [$calendarIds, $today, $today], + [ArrayParameterType::INTEGER, \PDO::PARAM_INT, \PDO::PARAM_INT], + )->fetchFirstColumn(); + } + + return array_values(array_unique(array_map('intval', $rows))); + } + + /** + * @param list $eventIds + * + * @return list + */ + private function findTagButtons(array $eventIds): array + { + if ([] === $eventIds) { + return []; + } + + $rows = $this->connection->executeQuery( + <<<'SQL' + SELECT t.id, t.tag, COUNT(DISTINCT r.item_id) AS total + FROM tl_tags_rel r + INNER JOIN tl_tags t ON t.id=r.tag_id + WHERE r.ptable='tl_calendar_events' + AND r.field='tags' + AND r.item_id IN (?) + GROUP BY t.id, t.tag + ORDER BY t.tag ASC + SQL, + [$eventIds], + [ArrayParameterType::INTEGER], + )->fetchAllAssociative(); + + $items = []; + + foreach ($rows as $row) { + $items[] = [ + 'id' => (int) ($row['id'] ?? 0), + 'title' => (string) ($row['tag'] ?? ''), + 'count' => (int) ($row['total'] ?? 0), + ]; + } + + return $items; + } + + /** + * @param list $eventIds + * + * @return list + */ + private function findLocations(array $eventIds): array + { + if ([] === $eventIds) { + return []; + } + + $rows = $this->connection->executeQuery( + <<<'SQL' + SELECT l.id, l.title, COUNT(e.id) AS total + FROM tl_calendar_events e + INNER JOIN tl_location l ON l.id=e.location + WHERE e.id IN (?) + AND e.location>0 + GROUP BY l.id, l.title + ORDER BY l.title ASC + SQL, + [$eventIds], + [ArrayParameterType::INTEGER], + )->fetchAllAssociative(); + + $items = []; + + foreach ($rows as $row) { + $items[] = [ + 'id' => (int) ($row['id'] ?? 0), + 'title' => (string) ($row['title'] ?? ''), + 'count' => (int) ($row['total'] ?? 0), + ]; + } + + return $items; + } + + /** + * @param list $eventIds + * + * @return list + */ + private function findOrganizations(array $eventIds): array + { + if ([] === $eventIds) { + return []; + } + + $rows = $this->connection->executeQuery( + <<<'SQL' + SELECT o.id, o.title, COUNT(DISTINCT rel.event_id) AS total + FROM tl_calendar_events_organization rel + INNER JOIN tl_organization o ON o.id=rel.organization_id + WHERE rel.event_id IN (?) + GROUP BY o.id, o.title + ORDER BY o.title ASC + SQL, + [$eventIds], + [ArrayParameterType::INTEGER], + )->fetchAllAssociative(); + + $items = []; + + foreach ($rows as $row) { + $items[] = [ + 'id' => (int) ($row['id'] ?? 0), + 'title' => (string) ($row['title'] ?? ''), + 'count' => (int) ($row['total'] ?? 0), + ]; + } + + return $items; + } +}