Add event filter frontend module with server-side aggregates

This commit is contained in:
Jürgen Mummert
2026-02-22 11:01:12 +01:00
parent ba10d1c403
commit 0685f8eeb1
5 changed files with 223 additions and 0 deletions
@@ -0,0 +1,193 @@
<?php
declare(strict_types=1);
namespace MummertMedia\EventManagerBundle\Controller\Frontend;
use Contao\CoreBundle\Controller\FrontendModule\AbstractFrontendModuleController;
use Contao\CoreBundle\DependencyInjection\Attribute\AsFrontendModule;
use Contao\CoreBundle\Twig\FragmentTemplate;
use Contao\ModuleModel;
use Contao\StringUtil;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
#[AsFrontendModule(type: 'event_filter', category: 'eventmanager', template: 'frontend/event_filter')]
class EventFilterController extends AbstractFrontendModuleController
{
public function __construct(
private readonly Connection $connection,
) {
}
protected function getResponse(FragmentTemplate $template, ModuleModel $model, Request $request): Response
{
$calendarIds = array_map('intval', StringUtil::deserialize($model->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<int> $calendarIds
*
* @return list<int>
*/
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<int> $eventIds
*
* @return list<array{id:int,title:string,count:int}>
*/
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<int> $eventIds
*
* @return list<array{id:int,title:string,count:int}>
*/
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<int> $eventIds
*
* @return list<array{id:int,title:string,count:int}>
*/
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;
}
}