Completed
Push — master ( b32b81...2b4c6f )
by Stefano
16s queued 11s
created

ExportComponent::csv()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
1
<?php
2
namespace App\Controller\Component;
3
4
use Cake\Controller\Component;
5
use Cake\Utility\Hash;
6
use PhpOffice\PhpSpreadsheet\IOFactory;
7
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8
9
/**
10
 * Export component. Utility to create csv, ods and xlsx spreadsheets.
11
 */
12
class ExportComponent extends Component
13
{
14
    /**
15
     * Allowed formats for export
16
     *
17
     * @var array
18
     */
19
    const ALLOWED_FORMATS = ['csv', 'ods', 'xlsx'];
20
21
    /**
22
     * Spreadsheet columns int/letter mapping.
23
     *
24
     * @var array
25
     */
26
    public const LETTERS = [
27
        1 => 'A', 2 => 'B', 3 => 'C', 4 => 'D', 5 => 'E', 6 => 'F', 7 => 'G', 8 => 'H', 9 => 'I', 10 => 'J',
28
        11 => 'K', 12 => 'L', 13 => 'M', 14 => 'N', 15 => 'O', 16 => 'P', 17 => 'Q', 18 => 'R', 19 => 'S', 20 => 'T',
29
        21 => 'U', 22 => 'V', 23 => 'W', 24 => 'X', 25 => 'Y', 26 => 'Z',
30
    ];
31
32
    /**
33
     * Default properties for spreadsheet
34
     *
35
     * @var array
36
     */
37
    protected $defaultSpreadheetProperties = [
38
        'creator' => 'BEdita Manager',
39
        'lastModifiedBy' => 'BEdita Manager',
40
        'title' => '',
41
        'subject' => '',
42
        'description' => '',
43
        'keywords' => '',
44
        'category' => '',
45
    ];
46
47
    /**
48
     * Check if format is allowed
49
     *
50
     * @param string $format The format
51
     * @return bool
52
     */
53
    public function checkFormat(string $format): bool
54
    {
55
        return in_array($format, static::ALLOWED_FORMATS);
56
    }
57
58
    /**
59
     * Create spreadsheet with data from rows, using properties.
60
     *
61
     * @param string $format The format
62
     * @param array $rows The data
63
     * @param string $filename The file name
64
     * @param array $properties The properties
65
     * @return array
66
     */
67
    public function format(string $format, array $rows, string $filename, array $properties = []): array
68
    {
69
        $spreadsheet = new Spreadsheet();
70
71
        // set properties
72
        $properties = array_merge($this->defaultSpreadheetProperties, $properties);
73
        foreach ($properties as $property => $value) {
74
            $spreadsheet->getProperties()->{sprintf('set%s', ucfirst($property))}($value);
75
        }
76
77
        // create the worksheet
78
        $spreadsheet->setActiveSheetIndex(0);
79
80
        // set first row, column names
81
        if (!empty($rows)) {
82
            $first = array_values(array_shift($rows));
83
            foreach ($first as $k => $v) {
84
                $spreadsheet->getActiveSheet()->setCellValue(sprintf('%s1', $this->column($k + 1)), $v);
85
            }
86
        }
87
88
        // fill data
89
        $spreadsheet->getActiveSheet()->fromArray($rows, null, 'A2');
90
91
        // set autofilter
92
        $spreadsheet->getActiveSheet()->setAutoFilter($spreadsheet->getActiveSheet()->calculateWorksheetDimension());
93
94
        return $this->{$format}($spreadsheet, $filename);
95
    }
96
97
    /**
98
     * Calculate excel column name from integer number of column.
99
     *
100
     * @param int $num The number
101
     * @return string
102
     */
103
    protected function column(int $num): string
104
    {
105
        if (array_key_exists($num, self::LETTERS)) {
106
            return self::LETTERS[$num];
107
        }
108
        $div = intdiv($num, 26);
109
        $mod = $num % 26;
110
        if ($mod === 0) {
111
            $firstLetter = self::LETTERS[$div - 1];
112
            $secondLetter = 'Z';
113
        } else {
114
            $firstLetter = self::LETTERS[$div];
115
            $secondLetter = self::LETTERS[$mod];
116
        }
117
118
        return $firstLetter . $secondLetter;
119
    }
120
121
    /**
122
     * Create spreadsheet file into memory and redirect output to download it as csv
123
     *
124
     * @param Spreadsheet $spreadsheet The spreadsheet
125
     * @param string $filename The file name
126
     * @return array
127
     */
128
    public function csv(Spreadsheet $spreadsheet, string $filename = 'export.csv'): array
129
    {
130
        $options = compact('filename') + ['format' => 'text/csv'];
131
132
        return $this->download($spreadsheet, 'Csv', $options);
133
    }
134
135
    /**
136
     * Create spreadsheet file into memory and redirect output to download it as ods
137
     *
138
     * @param Spreadsheet $spreadsheet The spreadsheet
139
     * @param string $filename The file name
140
     * @return array
141
     */
142
    public function ods(Spreadsheet $spreadsheet, string $filename = 'export.ods'): array
143
    {
144
        $options = compact('filename') + ['format' => 'application/vnd.oasis.opendocument.spreadsheet'];
145
146
        return $this->download($spreadsheet, 'Ods', $options);
147
    }
148
149
    /**
150
     * Create spreadsheet file into memory and redirect output to download it as xlsx
151
     *
152
     * @param Spreadsheet $spreadsheet The spreadsheet
153
     * @param string $filename The file name
154
     * @return array
155
     */
156
    public function xlsx(Spreadsheet $spreadsheet, string $filename = 'export.xlsx'): array
157
    {
158
        $options = compact('filename') + ['format' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'];
159
160
        return $this->download($spreadsheet, 'Xlsx', $options);
161
    }
162
163
    /**
164
     * Create spreadsheet, write to a temporary file, set content and content type and return them.
165
     *
166
     * @param Spreadsheet $spreadsheet The spreadsheet
167
     * @param string $extension The extension, can be 'Csv', 'Ods', 'Pdf', 'Xls', 'Xlsx'
168
     * @param array $options The options
169
     * @return array
170
     */
171
    protected function download(Spreadsheet $spreadsheet, string $extension, array $options): array
172
    {
173
        $tmpfilename = tempnam('/tmp', Hash::get($options, 'filename'));
174
        $writer = IOFactory::createWriter($spreadsheet, $extension);
175
        $writer->save($tmpfilename);
176
        $content = file_get_contents($tmpfilename);
177
        $contentType = Hash::get($options, 'format');
178
        unlink($tmpfilename);
179
180
        return compact('content', 'contentType');
181
    }
182
}
183