Total Complexity | 66 |
Total Lines | 498 |
Duplicated Lines | 0 % |
Changes | 8 | ||
Bugs | 3 | Features | 0 |
Complex classes like ExcelGridFieldExportButton often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ExcelGridFieldExportButton, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
23 | class ExcelGridFieldExportButton implements |
||
24 | GridField_HTMLProvider, |
||
25 | GridField_ActionProvider, |
||
26 | GridField_URLHandler |
||
27 | { |
||
28 | /** |
||
29 | * @var array Map of a property name on the exported objects, with values being the column title in the file. |
||
30 | * Note that titles are only used when {@link $hasHeader} is set to TRUE. |
||
31 | */ |
||
32 | protected $exportColumns; |
||
33 | |||
34 | /** |
||
35 | * Fragment to write the button to |
||
36 | */ |
||
37 | protected $targetFragment; |
||
38 | |||
39 | /** |
||
40 | * @var boolean |
||
41 | */ |
||
42 | protected $hasHeader = true; |
||
43 | |||
44 | /** |
||
45 | * @var string |
||
46 | */ |
||
47 | protected $exportType = 'xlsx'; |
||
48 | |||
49 | /** |
||
50 | * @var string |
||
51 | */ |
||
52 | protected $exportName = null; |
||
53 | |||
54 | /** |
||
55 | * |
||
56 | * @var string |
||
57 | */ |
||
58 | protected $buttonTitle = null; |
||
59 | |||
60 | /** |
||
61 | * |
||
62 | * @var bool |
||
63 | */ |
||
64 | protected $checkCanView = true; |
||
65 | |||
66 | /** |
||
67 | * @var bool |
||
68 | */ |
||
69 | protected $isLimited = true; |
||
70 | |||
71 | /** |
||
72 | * |
||
73 | * @var array |
||
74 | */ |
||
75 | protected $listFilters = array(); |
||
76 | |||
77 | /** |
||
78 | * |
||
79 | * @var callable |
||
80 | */ |
||
81 | protected $afterExportCallback; |
||
82 | |||
83 | /** |
||
84 | * @param string $targetFragment The HTML fragment to write the button into |
||
85 | * @param array $exportColumns The columns to include in the export |
||
86 | */ |
||
87 | public function __construct($targetFragment = "after", $exportColumns = null) |
||
88 | { |
||
89 | $this->targetFragment = $targetFragment; |
||
90 | $this->exportColumns = $exportColumns; |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * @param GridField $gridField |
||
95 | * @return string |
||
96 | */ |
||
97 | public function getActionName($gridField) |
||
98 | { |
||
99 | $name = strtolower($gridField->getName()); |
||
100 | return 'excelexport_' . $name; |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * Place the export button in a <p> tag below the field |
||
105 | */ |
||
106 | public function getHTMLFragments($gridField) |
||
107 | { |
||
108 | $title = $this->buttonTitle ? $this->buttonTitle : _t( |
||
109 | 'ExcelImportExport.XLSEXPORT', |
||
110 | 'Export to Excel' |
||
111 | ); |
||
112 | |||
113 | $name = $this->getActionName($gridField); |
||
114 | |||
115 | $button = new GridField_FormAction( |
||
116 | $gridField, |
||
117 | $name, |
||
118 | $title, |
||
119 | $name, |
||
120 | null |
||
121 | ); |
||
122 | $button->addExtraClass('btn btn-secondary no-ajax font-icon-down-circled action_export'); |
||
123 | $button->setForm($gridField->getForm()); |
||
124 | |||
125 | return array( |
||
126 | $this->targetFragment => $button->Field() |
||
127 | ); |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * export is an action button |
||
132 | */ |
||
133 | public function getActions($gridField) |
||
134 | { |
||
135 | return array($this->getActionName($gridField)); |
||
136 | } |
||
137 | |||
138 | public function handleAction( |
||
139 | GridField $gridField, |
||
140 | $actionName, |
||
141 | $arguments, |
||
142 | $data |
||
143 | ) { |
||
144 | if (in_array($actionName, $this->getActions($gridField))) { |
||
145 | return $this->handleExport($gridField); |
||
|
|||
146 | } |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * it is also a URL |
||
151 | */ |
||
152 | public function getURLHandlers($gridField) |
||
153 | { |
||
154 | return array($this->getActionName($gridField) => 'handleExport'); |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * Handle the export, for both the action button and the URL |
||
159 | */ |
||
160 | public function handleExport($gridField, $request = null) |
||
161 | { |
||
162 | $now = Date("Ymd_Hi"); |
||
163 | |||
164 | if ($excel = $this->generateExportFileData($gridField)) { |
||
165 | $ext = $this->exportType; |
||
166 | $name = $this->exportName; |
||
167 | $fileName = "$name-$now.$ext"; |
||
168 | |||
169 | switch ($ext) { |
||
170 | case 'xls': |
||
171 | $writer = IOFactory::createWriter($excel, 'Xls'); |
||
172 | break; |
||
173 | case 'xlsx': |
||
174 | $writer = IOFactory::createWriter($excel, 'Xlsx'); |
||
175 | break; |
||
176 | default: |
||
177 | throw new Exception("$ext is not supported"); |
||
178 | } |
||
179 | if ($this->afterExportCallback) { |
||
180 | $func = $this->afterExportCallback; |
||
181 | $func(); |
||
182 | } |
||
183 | |||
184 | header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); |
||
185 | header("Cache-Control: post-check=0, pre-check=0", false); |
||
186 | header("Pragma: no-cache"); |
||
187 | header('Content-type: application/vnd.ms-excel'); |
||
188 | header('Content-Disposition: attachment; filename="' . $fileName . '"'); |
||
189 | |||
190 | $writer->save('php://output'); |
||
191 | exit(); |
||
192 | } |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Generate export fields for Excel. |
||
197 | * |
||
198 | * @param GridField $gridField |
||
199 | * @return Spreadsheet |
||
200 | */ |
||
201 | public function generateExportFileData($gridField) |
||
202 | { |
||
203 | $class = $gridField->getModelClass(); |
||
204 | $columns = ($this->exportColumns) ? $this->exportColumns : ExcelImportExport::exportFieldsForClass($class); |
||
205 | |||
206 | $singl = singleton($class); |
||
207 | |||
208 | $plural = $class ? $singl->i18n_plural_name() : ''; |
||
209 | |||
210 | $filter = new FileNameFilter; |
||
211 | if ($this->exportName) { |
||
212 | $this->exportName = $filter->filter($this->exportName); |
||
213 | } else { |
||
214 | $this->exportName = $filter->filter('export-' . $plural); |
||
215 | } |
||
216 | |||
217 | $excel = new Spreadsheet(); |
||
218 | $excelProperties = $excel->getProperties(); |
||
219 | $excelProperties->setTitle($this->exportName); |
||
220 | |||
221 | $sheet = $excel->getActiveSheet(); |
||
222 | if ($plural) { |
||
223 | $sheet->setTitle($plural); |
||
224 | } |
||
225 | |||
226 | $row = 1; |
||
227 | $col = 1; |
||
228 | |||
229 | if ($this->hasHeader) { |
||
230 | $headers = array(); |
||
231 | |||
232 | // determine the headers. If a field is callable (e.g. anonymous function) then use the |
||
233 | // source name as the header instead |
||
234 | foreach ($columns as $columnSource => $columnHeader) { |
||
235 | $headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) |
||
236 | ? $columnSource : $columnHeader; |
||
237 | } |
||
238 | |||
239 | foreach ($headers as $header) { |
||
240 | $sheet->setCellValue([$col, $row], $header); |
||
241 | $col++; |
||
242 | } |
||
243 | |||
244 | $endcol = Coordinate::stringFromColumnIndex($col - 1); |
||
245 | $sheet->setAutoFilter("A1:{$endcol}1"); |
||
246 | $sheet->getStyle("A1:{$endcol}1")->getFont()->setBold(true); |
||
247 | |||
248 | $col = 1; |
||
249 | $row++; |
||
250 | } |
||
251 | |||
252 | // Autosize |
||
253 | $cellIterator = $sheet->getRowIterator()->current()->getCellIterator(); |
||
254 | try { |
||
255 | $cellIterator->setIterateOnlyExistingCells(true); |
||
256 | } catch (Exception $ex) { |
||
257 | // Ignore exceptions |
||
258 | } |
||
259 | foreach ($cellIterator as $cell) { |
||
260 | $sheet->getColumnDimension($cell->getColumn())->setAutoSize(true); |
||
261 | } |
||
262 | |||
263 | //Remove GridFieldPaginator as we're going to export the entire list. |
||
264 | $gridField->getConfig()->removeComponentsByType(GridFieldPaginator::class); |
||
265 | |||
266 | $items = $gridField->getManipulatedList(); |
||
267 | |||
268 | // @todo should GridFieldComponents change behaviour based on whether others are available in the config? |
||
269 | foreach ($gridField->getConfig()->getComponents() as $component) { |
||
270 | if ($component instanceof GridFieldFilterHeader || $component instanceof GridFieldSortableHeader) { |
||
271 | $items = $component->getManipulatedData($gridField, $items); |
||
272 | } |
||
273 | } |
||
274 | |||
275 | $list = $items; |
||
276 | $limit = ExcelImportExport::$limit_exports; |
||
277 | if ($items instanceof DataList && $this->isLimited && $limit > 0) { |
||
278 | $list = $items->limit($limit); |
||
279 | if (!empty($this->listFilters)) { |
||
280 | $list = $list->filter($this->listFilters); |
||
281 | } |
||
282 | } |
||
283 | |||
284 | if (!$list) { |
||
285 | return $excel; |
||
286 | } |
||
287 | |||
288 | $exportFormat = ExcelImportExport::config()->export_format; |
||
289 | |||
290 | foreach ($list as $item) { |
||
291 | if ($this->checkCanView) { |
||
292 | $canView = true; |
||
293 | if ($item->hasMethod('canView') && !$item->canView()) { |
||
294 | $canView = false; |
||
295 | } |
||
296 | if (!$canView) { |
||
297 | continue; |
||
298 | } |
||
299 | } |
||
300 | foreach ($columns as $columnSource => $columnHeader) { |
||
301 | if (!is_string($columnHeader) && is_callable($columnHeader)) { |
||
302 | if ($item->hasMethod($columnSource)) { |
||
303 | $relObj = $item->{$columnSource}(); |
||
304 | } else { |
||
305 | $relObj = $item->relObject($columnSource); |
||
306 | } |
||
307 | |||
308 | $value = $columnHeader($relObj); |
||
309 | } else { |
||
310 | if (is_string($columnSource)) { |
||
311 | // It can be a method |
||
312 | if (strpos($columnSource, '(') !== false) { |
||
313 | $matches = []; |
||
314 | preg_match('/([a-zA-Z]*)\((.*)\)/', $columnSource, $matches); |
||
315 | $func = $matches[1]; |
||
316 | $params = explode(",", $matches[2]); |
||
317 | // Support only one param for now |
||
318 | $value = $item->$func($params[0]); |
||
319 | } else { |
||
320 | if (array_key_exists($columnSource, $exportFormat)) { |
||
321 | $format = $exportFormat[$columnSource]; |
||
322 | $value = $item->dbObject($columnSource)->$format(); |
||
323 | } else { |
||
324 | $value = $gridField->getDataFieldValue($item, $columnSource); |
||
325 | } |
||
326 | } |
||
327 | } else { |
||
328 | // We can also use a simple dot notation |
||
329 | $parts = explode(".", $columnHeader); |
||
330 | if (count($parts) == 1) { |
||
331 | $value = $item->$columnHeader; |
||
332 | } else { |
||
333 | $value = $item->relObject($parts[0]); |
||
334 | if ($value) { |
||
335 | $relObjField = $parts[1]; |
||
336 | $value = $value->$relObjField; |
||
337 | } |
||
338 | } |
||
339 | } |
||
340 | } |
||
341 | |||
342 | $sheet->setCellValue([$col, $row], $value); |
||
343 | $col++; |
||
344 | } |
||
345 | |||
346 | if ($item->hasMethod('destroy')) { |
||
347 | $item->destroy(); |
||
348 | } |
||
349 | |||
350 | $col = 1; |
||
351 | $row++; |
||
352 | } |
||
353 | |||
354 | return $excel; |
||
355 | } |
||
356 | |||
357 | /** |
||
358 | * @return array |
||
359 | */ |
||
360 | public function getExportColumns() |
||
361 | { |
||
362 | return $this->exportColumns; |
||
363 | } |
||
364 | |||
365 | /** |
||
366 | * @param array |
||
367 | */ |
||
368 | public function setExportColumns($cols) |
||
369 | { |
||
370 | $this->exportColumns = $cols; |
||
371 | return $this; |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * @return boolean |
||
376 | */ |
||
377 | public function getHasHeader() |
||
378 | { |
||
379 | return $this->hasHeader; |
||
380 | } |
||
381 | |||
382 | /** |
||
383 | * @param boolean |
||
384 | */ |
||
385 | public function setHasHeader($bool) |
||
386 | { |
||
387 | $this->hasHeader = $bool; |
||
388 | return $this; |
||
389 | } |
||
390 | |||
391 | /** |
||
392 | * @return string |
||
393 | */ |
||
394 | public function getExportType() |
||
395 | { |
||
396 | return $this->exportType; |
||
397 | } |
||
398 | |||
399 | /** |
||
400 | * @param string xlsx (default) or xls |
||
401 | */ |
||
402 | public function setExportType($exportType) |
||
403 | { |
||
404 | $this->exportType = $exportType; |
||
405 | return $this; |
||
406 | } |
||
407 | |||
408 | /** |
||
409 | * @return string |
||
410 | */ |
||
411 | public function getExportName() |
||
412 | { |
||
413 | return $this->exportName; |
||
414 | } |
||
415 | |||
416 | /** |
||
417 | * @param string $exportName |
||
418 | * @return ExcelGridFieldExportButton |
||
419 | */ |
||
420 | public function setExportName($exportName) |
||
421 | { |
||
422 | $this->exportName = $exportName; |
||
423 | return $this; |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * @return string |
||
428 | */ |
||
429 | public function getButtonTitle() |
||
430 | { |
||
431 | return $this->buttonTitle; |
||
432 | } |
||
433 | |||
434 | /** |
||
435 | * @param string $buttonTitle |
||
436 | * @return ExcelGridFieldExportButton |
||
437 | */ |
||
438 | public function setButtonTitle($buttonTitle) |
||
439 | { |
||
440 | $this->buttonTitle = $buttonTitle; |
||
441 | return $this; |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * |
||
446 | * @return bool |
||
447 | */ |
||
448 | public function getCheckCanView() |
||
449 | { |
||
450 | return $this->checkCanView; |
||
451 | } |
||
452 | |||
453 | /** |
||
454 | * |
||
455 | * @param bool $checkCanView |
||
456 | * @return ExcelGridFieldExportButton |
||
457 | */ |
||
458 | public function setCheckCanView($checkCanView) |
||
459 | { |
||
460 | $this->checkCanView = $checkCanView; |
||
461 | return $this; |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * |
||
466 | * @return array |
||
467 | */ |
||
468 | public function getListFilters() |
||
469 | { |
||
470 | return $this->listFilters; |
||
471 | } |
||
472 | |||
473 | /** |
||
474 | * |
||
475 | * @param array $listFilters |
||
476 | * @return ExcelGridFieldExportButton |
||
477 | */ |
||
478 | public function setListFilters($listFilters) |
||
482 | } |
||
483 | |||
484 | /** |
||
485 | * |
||
486 | * @return callable |
||
487 | */ |
||
488 | public function getAfterExportCallback() |
||
491 | } |
||
492 | |||
493 | /** |
||
494 | * |
||
495 | * @param callable $afterExportCallback |
||
496 | * @return ExcelGridFieldExportButton |
||
497 | */ |
||
498 | public function setAfterExportCallback(callable $afterExportCallback) |
||
499 | { |
||
500 | $this->afterExportCallback = $afterExportCallback; |
||
501 | return $this; |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Get the value of isLimited |
||
506 | */ |
||
507 | public function getIsLimited(): bool |
||
510 | } |
||
511 | |||
512 | /** |
||
513 | * Set the value of isLimited |
||
514 | * |
||
515 | * @param bool $isLimited |
||
516 | */ |
||
517 | public function setIsLimited(bool $isLimited) |
||
518 | { |
||
519 | $this->isLimited = $isLimited; |
||
520 | return $this; |
||
521 | } |
||
522 | } |
||
523 |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.