Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
83 / 83
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
BarChart
100.00% covered (success)
100.00%
83 / 83
100.00% covered (success)
100.00%
5 / 5
19
100.00% covered (success)
100.00%
1 / 1
 render
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
5
 calculateChartGroups
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
4
 calculateYLabels
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 calculateBarChartClass
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 generateChartDemoData
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Helpers;
6
7use Carbon\CarbonImmutable;
8use DateTimeImmutable;
9
10class BarChart
11{
12    /**
13     * Renders a bar chart using the "components/barchart"  view.
14     *
15     * @param array<string> $rowLabels Map row key => row label
16     * @param array<string, string> $colors Map row key => color
17     * @param array<mixed, array> $data The chart data group key => [ row name => value ]
18     */
19    public static function render(
20        array $rowLabels,
21        array $colors,
22        array $data
23    ): string {
24        $groupLabels = [];
25        $max = 0;
26
27        foreach ($data as $groupKey => $groupData) {
28            $date = DateTimeImmutable::createFromFormat('Y-m-d', $groupKey);
29            $groupLabels[$groupKey] = $groupKey;
30
31            if ($date) {
32                $groupLabels[$groupKey] = $date->format(__('general.date'));
33            }
34
35            foreach ($rowLabels as $rowKey => $rowName) {
36                $max = max($max, $groupData[$rowKey]);
37            }
38        }
39
40        $roundedMax = $max === 0
41            ? 5
42            : (int) ceil($max / 5) * 5;
43
44        return view('components/barchart', [
45            'groups' => self::calculateChartGroups(
46                $rowLabels,
47                $colors,
48                $data,
49                $roundedMax,
50                $groupLabels
51            ),
52            'colors' => $colors,
53            'rowLabels' => $rowLabels,
54            'barChartClass' => self::calculateBarChartClass($rowLabels, $data),
55            'yLabels' => self::calculateYLabels($roundedMax),
56        ]);
57    }
58
59    private static function calculateChartGroups(
60        array $rowLabels,
61        array $colors,
62        array $data,
63        int $max,
64        array $groupLabels
65    ): array {
66        $chartGroups = [];
67
68        foreach ($data as $groupKey => $groupData) {
69            $group = [
70                'label' => $groupLabels[$groupKey],
71                'bars' => [],
72            ];
73
74            foreach ($rowLabels as $rowKey => $rowName) {
75                $value = $groupData[$rowKey];
76                $group['bars'][] = [
77                    'value' => $value,
78                    'title' => $group['label'] . "\n" . $rowName . ': ' . $value,
79                    'height' => $max === 0 ? '0%' : ($value / $max * 100) . '%',
80                    'bg' => $colors[$rowKey],
81                ];
82            }
83
84            $chartGroups[] = $group;
85        }
86
87        return $chartGroups;
88    }
89
90    /**
91     * @param int $max Max Y value
92     * @return array<int, array{label: string, bottom: string}>
93     */
94    private static function calculateYLabels(int $max): array
95    {
96        $step = $max / 5;
97        $yLabels = [];
98
99        for ($y = 0; $y <= $max; $y += $step) {
100            $yLabels[] = [
101                'label' => (string) $y,
102                'bottom' => $max === 0 ? '0%' : ($y / $max * 100) . '%',
103            ];
104        }
105
106        return $yLabels;
107    }
108
109    private static function calculateBarChartClass(array $rowLabels, array $data): string
110    {
111        $bars = count($data) * count($rowLabels);
112
113        if ($bars >= 50) {
114            return 'barchart-50';
115        }
116
117        if ($bars >= 40) {
118            return 'barchart-40';
119        }
120
121        if ($bars >= 20) {
122            return 'barchart-20';
123        }
124
125        return '';
126    }
127
128    /**
129     * Generates bar chart demo data.
130     *
131     * @param int $days Number of days to generate data for
132     * @return array ready to be passed to BarChart::render
133     */
134    public static function generateChartDemoData(int $days): array
135    {
136        $step = $days === 0 ? 0 : floor(10000 / $days + 1);
137        $now = CarbonImmutable::now();
138        $twoWeeksAgo = $now->subDays($days);
139        $current = $twoWeeksAgo;
140
141        $demoData = [];
142        $count = 1;
143
144        while ($current->isBefore($now)) {
145            $current_key = $current->format('Y-m-d');
146            $demoData[$current_key] = [
147                'day' => $current_key,
148                'count' => $step,
149                'sum' => $step * $count,
150            ];
151            $current = $current->addDay();
152            $count++;
153        }
154
155        return [
156            [
157                'count' => __('arrived'),
158                'sum'   => __('arrived sum'),
159            ],
160            [
161
162                'count' => '#090',
163                'sum'   => '#888',
164            ],
165            $demoData,
166        ];
167    }
168}