Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 135
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
OlzTermineUpcomingTile
0.00% covered (danger)
0.00%
0 / 135
0.00% covered (danger)
0.00%
0 / 7
210
0.00% covered (danger)
0.00%
0 / 1
 getRelevance
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getHtml
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
30
 renderProgramList
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
20
 renderWeekendsList
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 renderTrophyList
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 renderUpcomingTrainingsList
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 getNumberOfEntries
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3// =============================================================================
4// Zeigt eine Startseiten-Kachel mit den nächsten Terminen an.
5// =============================================================================
6
7namespace Olz\Startseite\Components\OlzTermineUpcomingTile;
8
9use Olz\Entity\Termine\TerminLabel;
10use Olz\Entity\Users\User;
11use Olz\Startseite\Components\AbstractOlzTile\AbstractOlzTile;
12
13class OlzTermineUpcomingTile extends AbstractOlzTile {
14    private ?\mysqli $db = null;
15    private ?int $this_year = null;
16
17    public function getRelevance(?User $user): float {
18        return 0.7;
19    }
20
21    public function getHtml(mixed $args): string {
22        $this->termineUtils()->loadTypeOptions();
23        $this->db = $this->dbUtils()->getDb();
24        $this->this_year = intval($this->dateUtils()->getCurrentDateInFormat('Y'));
25        $code_href = $this->envUtils()->getCodeHref();
26        $code_path = $this->envUtils()->getCodePath();
27        $today = $this->dateUtils()->getIsoToday();
28        $termin_label_repo = $this->entityManager()->getRepository(TerminLabel::class);
29
30        $termine_url = $this->termineUtils()->getUrl(['typ' => 'alle', 'datum' => 'bevorstehend']);
31
32        $out = <<<ZZZZZZZZZZ
33            <h3>
34                <a href='{$termine_url}&von=startseite' class='header-link'>
35                    <img src='{$code_href}assets/icns/termine_type_all_20.svg' alt='Termine' class='header-link-icon'>
36                    Termine
37                </a>
38            </h3>
39            ZZZZZZZZZZ;
40
41        $out .= "<div class='filters'>".implode(' ', [
42            $this->renderProgramList(),
43            $this->renderWeekendsList(),
44            $this->renderTrophyList(),
45            $this->renderUpcomingTrainingsList(),
46        ])."</div>";
47
48        $out .= "<ul class='links'>";
49        $res = $this->db->query(<<<ZZZZZZZZZZ
50            SELECT
51                t.id,
52                t.start_date as date,
53                t.title as title,
54                (
55                    SELECT l.id
56                    FROM 
57                        termin_label_map tl
58                        JOIN termin_labels l ON (l.id = tl.label_id)
59                    WHERE tl.termin_id = t.id
60                    ORDER BY l.position ASC
61                    LIMIT 1
62                ) as label_id
63            FROM termine t
64            WHERE t.on_off = '1' AND t.start_date >= '{$today}'
65            ORDER BY t.start_date ASC
66            LIMIT 7
67            ZZZZZZZZZZ);
68        // @phpstan-ignore-next-line
69        while ($row = $res->fetch_assoc()) {
70            $id = $row['id'];
71            // @phpstan-ignore-next-line
72            $date = $this->dateUtils()->compactDate($row['date']);
73            $title = $row['title'];
74            $label_id = $row['label_id'];
75            $label = $termin_label_repo->findOneBy(['id' => $label_id]);
76            $label_ident = $label?->getIdent();
77            $fallback_path = "{$code_path}assets/icns/termine_type_{$label_ident}_20.svg";
78            $fallback_href = is_file($fallback_path)
79                ? "{$code_href}assets/icns/termine_type_{$label_ident}_20.svg" : null;
80            $icon_href = $label?->getIcon() ? $label->getFileHref($label->getIcon()) : $fallback_href;
81            $icon_img = $icon_href ? "<img src='{$icon_href}' alt='' class='link-icon'>" : '';
82            $out .= <<<ZZZZZZZZZZ
83                    <li class='flex'>
84                        {$icon_img}
85                        <a href='{$code_href}termine/{$id}?von=startseite'>
86                            <b>{$date}</b>: {$title}
87                        </a>
88                    </li>
89                ZZZZZZZZZZ;
90        }
91        $out .= "</ul>";
92
93        return $out;
94    }
95
96    protected function renderProgramList(): string {
97        $code_href = $this->envUtils()->getCodeHref();
98        $icon = "{$code_href}assets/icns/termine_type_programm_20.svg";
99        $icon_img = "<img src='{$icon}' alt='' class='link-icon'>";
100        $this_year = $this->this_year;
101        $next_year = $this->this_year + 1;
102        $imminent_filter = [
103            ...$this->termineUtils()->getDefaultFilter(),
104            'typ' => 'programm',
105            'datum' => 'bevorstehend',
106        ];
107        $this_year_filter = [
108            ...$this->termineUtils()->getDefaultFilter(),
109            'typ' => 'programm',
110            'datum' => strval($this_year),
111        ];
112        $num_imminent = $this->getNumberOfEntries($imminent_filter);
113        $out = '';
114        if ($num_imminent > 0) {
115            $num_this_year = $this->getNumberOfEntries($this_year_filter);
116            $serialized_filter = $this->termineUtils()->serialize($this_year_filter);
117            $out .= <<<ZZZZZZZZZZ
118                <div class='filter'><a href='{$code_href}termine?filter={$serialized_filter}&von=startseite'>
119                    {$icon_img} Programm {$this_year}<span class='secondary'>({$num_this_year})</span>
120                </a></div>
121                ZZZZZZZZZZ;
122        }
123        $current_month = intval($this->dateUtils()->getCurrentDateInFormat('m'));
124        if ($current_month > 8) {
125            $next_year_filter = [
126                ...$this->termineUtils()->getDefaultFilter(),
127                'typ' => 'programm',
128                'datum' => strval($next_year),
129            ];
130            $num_next_year = $this->getNumberOfEntries($next_year_filter);
131
132            if ($num_next_year > 0) {
133                $serialized_filter = $this->termineUtils()->serialize($next_year_filter);
134                $out .= <<<ZZZZZZZZZZ
135                    <div class='filter'><a href='{$code_href}termine?filter={$serialized_filter}&von=startseite'>
136                        {$icon_img} Programm {$next_year}<span class='secondary'>({$num_next_year})</span>
137                    </a></div>
138                    ZZZZZZZZZZ;
139            }
140        }
141        return $out;
142    }
143
144    protected function renderWeekendsList(): string {
145        $code_href = $this->envUtils()->getCodeHref();
146        $icon = "{$code_href}assets/icns/termine_type_weekend_20.svg";
147        $icon_img = "<img src='{$icon}' alt='' class='link-icon'>";
148        $imminent_filter = [
149            ...$this->termineUtils()->getDefaultFilter(),
150            'typ' => 'weekend',
151            'datum' => 'bevorstehend',
152        ];
153        $num_imminent = $this->getNumberOfEntries($imminent_filter);
154        $serialized_filter = $this->termineUtils()->serialize($imminent_filter);
155        return <<<ZZZZZZZZZZ
156            <div class='filter'><a href='{$code_href}termine?filter={$serialized_filter}&von=startseite'>
157                {$icon_img} Weekends<span class='secondary'>({$num_imminent})</span>
158            </a></div>
159            ZZZZZZZZZZ;
160    }
161
162    protected function renderTrophyList(): string {
163        $code_href = $this->envUtils()->getCodeHref();
164        $icon = "{$code_href}assets/icns/termine_type_trophy_20.svg";
165        $icon_img = "<img src='{$icon}' alt='' class='link-icon'>";
166        $this_year = $this->this_year;
167        $this_year_filter = [
168            ...$this->termineUtils()->getDefaultFilter(),
169            'typ' => 'trophy',
170            'datum' => strval($this_year),
171        ];
172        $num_this_year = $this->getNumberOfEntries($this_year_filter);
173        $serialized_filter = $this->termineUtils()->serialize($this_year_filter);
174        return <<<ZZZZZZZZZZ
175            <div class='filter'><a href='{$code_href}termine?filter={$serialized_filter}&von=startseite'>
176                {$icon_img} OLZ Trophy<span class='secondary'>({$num_this_year})</span>
177            </a></div>
178            ZZZZZZZZZZ;
179    }
180
181    protected function renderUpcomingTrainingsList(): string {
182        $code_href = $this->envUtils()->getCodeHref();
183        $icon = "{$code_href}assets/icns/termine_type_training_20.svg";
184        $icon_img = "<img src='{$icon}' alt='' class='link-icon'>";
185        $imminent_filter = [
186            ...$this->termineUtils()->getDefaultFilter(),
187            'typ' => 'training',
188            'datum' => 'bevorstehend',
189        ];
190        $serialized_filter = $this->termineUtils()->serialize($imminent_filter);
191        return <<<ZZZZZZZZZZ
192            <div class='filter'><a href='{$code_href}termine?filter={$serialized_filter}&von=startseite'>
193                {$icon_img} Trainings
194            </a></div>
195            ZZZZZZZZZZ;
196    }
197
198    /** @param array{typ?: string, datum?: string, archiv?: string} $filter */
199    protected function getNumberOfEntries(array $filter): int {
200        $date_filter = $this->termineUtils()->getSqlDateRangeFilter($filter, 'c');
201        $type_filter = $this->termineUtils()->getSqlTypeFilter($filter, 'c');
202        $filter_sql = "({$date_filter}) AND ({$type_filter})";
203        $sql = <<<ZZZZZZZZZZ
204            SELECT * 
205            FROM (
206                SELECT
207                    t.id AS id,
208                    t.start_date AS start_date,
209                    t.end_date AS end_date,
210                    (
211                        SELECT GROUP_CONCAT(l.ident ORDER BY l.position ASC SEPARATOR ' ')
212                        FROM
213                            termin_label_map tl
214                            JOIN termin_labels l ON (l.id = tl.label_id)
215                        WHERE tl.termin_id = t.id
216                        GROUP BY t.id
217                    ) as typ
218                FROM termine t
219            ) AS c
220            WHERE {$filter_sql}
221            ZZZZZZZZZZ;
222        $res = $this->db?->query($sql);
223        // @phpstan-ignore-next-line
224        return $res->num_rows;
225    }
226}