1
|
|
|
<?php |
2
|
|
|
namespace Mathielen\ReportWriteEngine\Engine; |
3
|
|
|
|
4
|
|
|
use Mathielen\ReportWriteEngine\Engine\Compiler\ReportDataCompiler; |
5
|
|
|
use Psr\Log\LoggerInterface; |
6
|
|
|
use Psr\Log\NullLogger; |
7
|
|
|
|
8
|
|
|
class ReportRenderer |
9
|
|
|
{ |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* @var RendererRepository |
13
|
|
|
*/ |
14
|
|
|
private $rendererRepository; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var ReportDataCompiler |
18
|
|
|
*/ |
19
|
|
|
private $reportDataCompiler; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @var array |
23
|
|
|
*/ |
24
|
|
|
private $namedRanges; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var LoggerInterface |
28
|
|
|
*/ |
29
|
|
|
private $logger; |
30
|
|
|
|
31
|
|
View Code Duplication |
public function __construct( |
|
|
|
|
32
|
|
|
RendererRepository $rendererRepository, |
33
|
|
|
ReportDataCompiler $reportDataCompiler, |
34
|
|
|
array $namedRanges, |
35
|
|
|
LoggerInterface $logger = null) |
36
|
|
|
{ |
37
|
|
|
$this->rendererRepository = $rendererRepository; |
38
|
|
|
$this->reportDataCompiler = $reportDataCompiler; |
39
|
|
|
$this->namedRanges = $namedRanges; |
40
|
|
|
$this->logger = $logger ? $logger : new NullLogger(); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
private static function isDeepArray(array $array) |
44
|
|
|
{ |
45
|
|
|
return !empty($array) && is_array(array_values($array)[0]); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @return Canvas |
50
|
|
|
*/ |
51
|
|
|
public function render(array $data) |
52
|
|
|
{ |
53
|
|
|
$canvas = new Canvas($this->logger); |
54
|
|
|
|
55
|
|
|
foreach ($data as $node => $item) { |
56
|
|
|
if (is_array($item)) { |
57
|
|
|
$itemCanvas = $this->renderItem($item, $node); |
58
|
|
|
|
59
|
|
|
$this->rendererRepository |
60
|
|
|
->getRendererForNode($node) |
61
|
|
|
->render($itemCanvas, $canvas); |
62
|
|
|
} else { |
63
|
|
|
throw new \RuntimeException("Every entry of the first level of data must be an array."); |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
return $canvas; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
private function renderList(array $list, $namedRangeName) |
71
|
|
|
{ |
72
|
|
|
$this->logger->debug("Rendering list $namedRangeName"); |
73
|
|
|
|
74
|
|
|
$canvas = new Canvas($this->logger); |
75
|
|
|
foreach ($list as $item) { |
76
|
|
|
$itemCanvas = $this->renderItem($item, $namedRangeName); |
77
|
|
|
|
78
|
|
|
$this->rendererRepository |
79
|
|
|
->getRendererForNode($namedRangeName) |
80
|
|
|
->render($itemCanvas, $canvas); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
return $canvas; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
private function calculateOffsetY($namedRangeParent, $namedRangeChild) |
87
|
|
|
{ |
88
|
|
|
$minParent = min(array_keys($namedRangeParent)); |
89
|
|
|
$minChild = min(array_keys($namedRangeChild)); |
90
|
|
|
|
91
|
|
|
return $minChild - $minParent; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
private function calculateOffsetX($namedRangeParent, $namedRangeChild) |
95
|
|
|
{ |
96
|
|
|
$firstRowChild = $namedRangeChild[array_keys($namedRangeChild)[0]]; |
97
|
|
|
$firstRowParent = $namedRangeParent[array_keys($namedRangeParent)[0]]; |
98
|
|
|
|
99
|
|
|
$offsetX = min(array_keys($firstRowChild)) - min(array_keys($firstRowParent)); |
100
|
|
|
|
101
|
|
|
return $offsetX; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* @return Canvas |
106
|
|
|
*/ |
107
|
|
|
private function renderItem(array $data, $namedRangeName) |
108
|
|
|
{ |
109
|
|
|
$this->logger->debug("[START] Rendering START for item $namedRangeName"); |
110
|
|
|
|
111
|
|
|
$data = array_change_key_case($data, CASE_UPPER); |
112
|
|
|
$namedRangeName = strtoupper($namedRangeName); |
113
|
|
|
$namedRange = $this->namedRanges[$namedRangeName]; |
114
|
|
|
|
115
|
|
|
if (!isset($namedRange)) { |
116
|
|
|
throw new \RuntimeException("Could not find named range $namedRangeName"); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
$canvas = new Canvas($this->logger); |
120
|
|
|
$compiledData = $this->reportDataCompiler->compile($data, $namedRange); |
121
|
|
|
|
122
|
|
|
//zero-base all data |
123
|
|
|
$compiledData = array_values($compiledData); |
124
|
|
|
foreach ($compiledData as &$row) { |
125
|
|
|
$row = array_values($row); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
$canvas->write($compiledData); |
129
|
|
|
|
130
|
|
|
$i = 0; |
131
|
|
|
foreach ($data as $node => $item) { |
132
|
|
|
if (is_array($item)) { |
133
|
|
|
$itemNamedRange = isset($this->namedRanges[$node]) ? $this->namedRanges[$node] : null; |
134
|
|
|
|
135
|
|
|
if (self::isDeepArray($item) && $itemNamedRange) { |
136
|
|
|
$this->logger->debug("[START] List and Insert operation for $node"); |
137
|
|
|
|
138
|
|
|
$y = $this->calculateOffsetY($namedRange, $itemNamedRange); |
139
|
|
|
$h = count($itemNamedRange); |
140
|
|
|
$x = $this->calculateOffsetX($namedRange, $itemNamedRange); |
141
|
|
|
//$w = count($this->namedRanges[$node][array_keys($this->namedRanges[$node])[0]]); |
142
|
|
|
$w = 0; |
143
|
|
|
|
144
|
|
|
$itemCanvas = $this->renderList($item, $node); |
145
|
|
|
|
146
|
|
|
$canvas->insert($itemCanvas, $y, $x, $h, $w); |
147
|
|
|
|
148
|
|
|
$this->logger->debug("[DONE] List and Insert operation for $node"); |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
$i++; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
$this->logger->debug("[DONE] Rendering for item $namedRangeName"); |
155
|
|
|
|
156
|
|
|
return $canvas; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
} |
160
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.