1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace luya\helpers; |
4
|
|
|
|
5
|
|
|
use luya\Exception; |
6
|
|
|
use yii\base\Model; |
7
|
|
|
use yii\db\QueryInterface; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Exporting into Formats. |
11
|
|
|
* |
12
|
|
|
* @author Basil Suter <[email protected]> |
13
|
|
|
* @since 1.0.0 |
14
|
|
|
*/ |
15
|
|
|
class ExportHelper |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* Export an Array or QueryInterface instance into a CSV formated string. |
19
|
|
|
* |
20
|
|
|
* @param array|QueryInterface $input The data to export into a csv |
21
|
|
|
* @param array $keys Defines which keys should be packed into the generated CSV. The defined keys does not change the sort behavior of the generated csv. |
22
|
|
|
* @param string $header Whether the column name should be set as header inside the csv or not. |
23
|
|
|
* @param array $options Options {@since 1.8.0} |
24
|
|
|
* + `sort`: boolean, whether they row should be sorted by its keys, default is true. |
25
|
|
|
* @return string The generated CSV as string. |
26
|
|
|
*/ |
27
|
|
|
public static function csv($input, array $keys = [], $header = true, array $options = []) |
28
|
|
|
{ |
29
|
|
|
$delimiter = ","; |
30
|
|
|
$input = self::transformInput($input); |
31
|
|
|
$array = self::generateContentArray($input, $keys, $header, $options); |
|
|
|
|
32
|
|
|
|
33
|
|
|
return self::generateOutputString($array, $delimiter); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Export an Array or QueryInterface instance into a Excel formatted string. |
38
|
|
|
* |
39
|
|
|
* @param array|QueryInterface $input |
40
|
|
|
* @param array $keys Defines which keys should be packed into the generated xlsx. The defined keys does not change the sort behavior of the generated xls. |
41
|
|
|
* @param bool $header |
42
|
|
|
* @param array $options Options {@since 1.8.0} |
43
|
|
|
* + `sort`: boolean, whether they row should be sorted by its keys, default is true. |
44
|
|
|
* @return mixed |
45
|
|
|
* @throws Exception |
46
|
|
|
* @since 1.0.4 |
47
|
|
|
*/ |
48
|
|
|
public static function xlsx($input, array $keys = [], $header = true, array $options = []) |
49
|
|
|
{ |
50
|
|
|
$input = self::transformInput($input); |
51
|
|
|
|
52
|
|
|
$array = self::generateContentArray($input, $keys, $header, $options); |
53
|
|
|
|
54
|
|
|
$writer = new XLSXWriter(); |
55
|
|
|
$writer->writeSheet($array); |
56
|
|
|
|
57
|
|
|
return $writer->writeToString(); |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Check type of input and return correct array. |
62
|
|
|
* |
63
|
|
|
* @param array|QueryInterface $input |
64
|
|
|
* @return array |
65
|
|
|
* @since 1.0.4 |
66
|
|
|
*/ |
67
|
|
|
protected static function transformInput($input) |
68
|
|
|
{ |
69
|
|
|
if ($input instanceof QueryInterface) { |
70
|
|
|
return $input->all(); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
return $input; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Generate content by rows. |
78
|
|
|
* |
79
|
|
|
* @param array $contentRows |
80
|
|
|
* @param string $delimiter |
|
|
|
|
81
|
|
|
* @param array $keys |
82
|
|
|
* @param bool $generateHeader |
83
|
|
|
* @param array $options Options {@since 1.8.0} |
84
|
|
|
* + `sort`: boolean, whether they row should be sorted by its keys, default is true. |
85
|
|
|
* @return array |
86
|
|
|
* @throws Exception |
87
|
|
|
* @since 1.0.4 |
88
|
|
|
*/ |
89
|
|
|
protected static function generateContentArray($contentRows, array $keys, $generateHeader = true, $options = []) |
90
|
|
|
{ |
91
|
|
|
if (is_scalar($contentRows)) { |
92
|
|
|
throw new Exception("Content must be either an array, object or traversable."); |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
$attributeKeys = $keys; |
96
|
|
|
$header = []; |
97
|
|
|
$rows = []; |
98
|
|
|
$i = 0; |
99
|
|
|
foreach ($contentRows as $content) { |
100
|
|
|
// handle rows content |
101
|
|
|
if (!empty($keys) && is_array($content)) { |
102
|
|
|
foreach ($content as $k => $v) { |
103
|
|
|
if (!in_array($k, $keys)) { |
104
|
|
|
unset($content[$k]); |
105
|
|
|
} |
106
|
|
|
} |
107
|
|
|
} elseif (!empty($keys) && is_object($content)) { |
108
|
|
|
$attributeKeys[get_class($content)] = $keys; |
109
|
|
|
} |
110
|
|
|
$row = ArrayHelper::toArray($content, $attributeKeys, false); |
111
|
|
|
|
112
|
|
|
if (ArrayHelper::getValue($options, 'sort', true)) { |
113
|
|
|
ksort($row); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$rows[$i] = $row; |
117
|
|
|
|
118
|
|
|
// handle header |
119
|
|
|
if ($i == 0 && $generateHeader) { |
120
|
|
|
if ($content instanceof Model) { |
121
|
|
|
/** @var Model $content */ |
122
|
|
|
foreach ($content as $k => $v) { |
123
|
|
|
if (empty($keys)) { |
124
|
|
|
$header[$k] = $content->getAttributeLabel($k); |
125
|
|
|
} elseif (in_array($k, $keys)) { |
126
|
|
|
$header[$k] = $content->getAttributeLabel($k); |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
} else { |
130
|
|
|
$header = array_keys($rows[0]); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
if (ArrayHelper::getValue($options, 'sort', true)) { |
134
|
|
|
ksort($header); |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
unset($row); |
139
|
|
|
gc_collect_cycles(); |
140
|
|
|
$i++; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
if ($generateHeader) { |
144
|
|
|
return ArrayHelper::merge([$header], $rows); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return $rows; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* Generate the output string with delimiters. |
152
|
|
|
* |
153
|
|
|
* @param array $input |
154
|
|
|
* @param string $delimiter |
155
|
|
|
* @return null|string |
156
|
|
|
* @since 1.0.4 |
157
|
|
|
*/ |
158
|
|
|
protected static function generateOutputString(array $input, $delimiter) |
159
|
|
|
{ |
160
|
|
|
$output = null; |
161
|
|
|
foreach ($input as $row) { |
162
|
|
|
$output.= self::generateRow($row, $delimiter, '"'); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
return $output; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Generate a row by its items. |
170
|
|
|
* |
171
|
|
|
* @param array $row |
172
|
|
|
* @param string $delimiter |
173
|
|
|
* @param string $enclose |
174
|
|
|
* @return string |
175
|
|
|
* @since 1.0.4 |
176
|
|
|
*/ |
177
|
|
|
protected static function generateRow(array $row, $delimiter, $enclose) |
178
|
|
|
{ |
179
|
|
|
array_walk($row, function (&$item) use ($enclose) { |
180
|
|
|
if (is_bool($item)) { |
181
|
|
|
$item = (int) $item; |
182
|
|
|
} elseif (is_null($item)) { |
183
|
|
|
$item = ''; |
184
|
|
|
} elseif (!is_scalar($item)) { |
185
|
|
|
$item = "[array]"; |
186
|
|
|
} |
187
|
|
|
$item = $enclose.str_replace([ |
188
|
|
|
'"', |
189
|
|
|
], [ |
190
|
|
|
'""', |
191
|
|
|
], $item).$enclose; |
192
|
|
|
}); |
193
|
|
|
|
194
|
|
|
return implode($delimiter, $row) . PHP_EOL; |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.