TWorkSheet::excelGroupLeft()   B
last analyzed

Complexity

Conditions 9
Paths 15

Size

Total Lines 46
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 28
c 1
b 0
f 0
nc 15
nop 2
dl 0
loc 46
rs 8.0555
1
<?php
2
/**
3
 * @name: TWorklSheet
4
 * @author: JiaMeng <[email protected]>
5
 * @file: Export.php
6
 * @Date: 2024/03/04 10:15
7
 */
8
namespace tinymeng\spreadsheet\Excel;
9
10
use PhpOffice\PhpSpreadsheet\Cell\DataType;
11
use tinymeng\spreadsheet\Util\ConstCode;
12
use tinymeng\tools\exception\StatusCode;
13
use tinymeng\tools\exception\TinymengException;
14
15
trait TWorkSheet{
16
17
    /**
18
     * sheet名称
19
     * @var
20
     */
21
    private $sheetName;
22
    /**
23
     * 查询数据
24
     * @var
25
     */
26
    private $data;
27
28
    /**
29
     * 定义默认列数
30
     * @var int
31
     */
32
    private $_col = 0;
33
    /**
34
     * 定义当前行数
35
     * @var int
36
     */
37
    private $_row = 1;
38
    /**
39
     * 定义所有字段
40
     * @var array
41
     */
42
    private $field = [];
43
44
    /**
45
     * 文件信息
46
     * @var array
47
     */
48
    private $fileTitle=[];
49
50
51
    /**
52
     * 左侧分组字段
53
     * @var array
54
     */
55
    private $group_left = [];
56
57
58
    /**
59
     * 获取sheet表格数目
60
     * @var
61
     */
62
    private $sheetCount = 1;
63
64
    /**
65
     * 字段映射方式
66
     * @var int
67
     */
68
    private $fieldMappingMethod = ConstCode::FIELD_MAPPING_METHOD_FIELD_CORRESPONDING_NAME;
69
70
    /**
71
     * @param $data
72
     * @return $this
73
     */
74
    public function setData($data){
75
        $this->data = $data;
76
        return $this;
77
    }
78
79
    /**
80
     * @param $data
81
     * @return $this
82
     */
83
    public function getData(){
84
        return $this->data;
85
    }
86
87
88
    /**
89
     * @return void
90
     * @throws \PhpOffice\PhpSpreadsheet\Exception
91
     */
92
    public function initWorkSheet()
93
    {
94
        $this->_col = 0;
95
        $this->_row = 1;
96
        $this->fileTitle = [];
97
        $this->data = [];
98
        $this->field = [];
99
        if($this->freezePane) $this->workSheet->freezePane($this->freezePane); //冻结窗格
100
    }
101
102
    /**
103
     * @param $fileTitle
104
     * @param $data
105
     * @return $this
106
     * @throws TinymengException
107
     * @throws \PhpOffice\PhpSpreadsheet\Exception
108
     */
109
    public function setWorkSheetData($fileTitle,$data)
110
    {
111
        if(isset($fileTitle['title_row']) || isset($fileTitle['group_left'])){
112
            /**
113
             * $fileTitle = [
114
             *       'title_row'=>1,
115
             *       'group_left'=>[],
116
             *       'title'=>[
117
             *           '姓名'=>'name'
118
             *       ],
119
             *  ];
120
             */
121
            $this->title_row = $fileTitle['title_row']??1;
0 ignored issues
show
Bug Best Practice introduced by
The property title_row does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
122
            $this->group_left = $fileTitle['group_left']??[];
123
            $titleData = $fileTitle['title']??[];
124
        }else{
125
            /**
126
             *  $fileTitle = [
127
             *       '姓名'=>'name',
128
             *  ];
129
             */
130
            $titleData = $fileTitle;
131
        }
132
        // 根据字段映射方式处理 title
133
        if ($this->fieldMappingMethod === ConstCode::FIELD_MAPPING_METHOD_FIELD_CORRESPONDING_NAME) {
134
            $this->fileTitle = array_flip($titleData);// 字段对应名称方式 - 需要将键值对调
135
        }else{
136
            $this->fileTitle = $titleData;// 名称对应字段方式 - 保持原样
137
        }
138
        $this->data = $data;
139
140
        /** 设置第一行格式 */
141
        if($this->mainTitleLine === true){
142
            $this->excelHeader();
143
        }
144
145
        /** 设置表头 **/
146
        $this->excelTitle();
147
148
        /** 获取列表里所有字段 **/
149
        foreach ($this->fileTitle as $key => $val){
150
            if(is_array($val)){
151
                foreach ($val as $k => $v){
152
                    $this->field[] = $v;
153
                }
154
            }else{
155
                $this->field[] = $val;
156
            }
157
        }
158
        /** 查询结果赋值 **/
159
        if(!empty($this->data)){
160
            $this->excelSetValue();
161
        }
162
        return $this;
163
    }
164
165
    /**
166
     * excelSetValue
167
     * @author: Tinymeng <[email protected]>
168
     * @time: 2022/2/22 11:43
169
     */
170
    public function excelSetValue(){
171
        if(empty($this->group_left)){ //判断左侧是否分组
172
            foreach ($this->data as $key => $val){
173
                $this->excelSetCellValue($val);
174
            }
175
        }else{   //根据设置分组字段进行分组
176
            /** 数据分组 **/
177
            $data = [];
178
            $group_left_count = count($this->group_left);
179
            if($group_left_count == 1){
180
                foreach ($this->data as $k => $v){
181
                    $data[$v[$this->group_left[0]]][] = $v;
182
                }
183
                foreach ($data as $k =>$v){
184
                    $data[$k] = [
185
                        'data' => $v,
186
                        'count' => count($v)
187
                    ];
188
                }
189
                $this->excelGroupLeft($data, $group_left_count);
190
            }elseif($group_left_count == 2){
191
                foreach ($this->data as $v) {
192
                    $data[$v[$this->group_left[0]]][$v[$this->group_left[1]]][] = $v;
193
                }
194
                $this->data = $this->arrayCount($data);
195
                $this->excelGroupLeft($this->data, $group_left_count);
196
            }else{
197
                throw new TinymengException(StatusCode::COMMON_PARAM_INVALID,
198
                    '左侧分组过多,导出失败!'
199
                );
200
            }
201
        }
202
    }
203
204
    /**
205
     * @return void
206
     * @throws \PhpOffice\PhpSpreadsheet\Exception
207
     */
208
    public function excelHeader(){
209
        $row = 1;
210
        $this->workSheet->setCellValue('A'.$row, $this->mainTitle);
211
        
212
        // 计算实际的标题列数
213
        $titleCount = 0;
214
        foreach ($this->fileTitle as $key => $val) {
215
            if (is_array($val)) {
216
                $titleCount += count($val); // 如果是数组,加上子项的数量
217
            } else {
218
                $titleCount++; // 如果是单个标题,加1
219
            }
220
        }
221
        
222
        // 使用实际的标题列数来合并单元格
223
        $this->workSheet->mergeCells('A'.$row.':'.$this->cellName($titleCount-1).$row);
0 ignored issues
show
Bug introduced by
It seems like cellName() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

223
        $this->workSheet->mergeCells('A'.$row.':'.$this->/** @scrutinizer ignore-call */ cellName($titleCount-1).$row);
Loading history...
224
    }
225
226
    /**
227
     * @return void
228
     * @throws \PhpOffice\PhpSpreadsheet\Exception
229
     */
230
    private function excelTitle(){
231
        if($this->mainTitleLine === true){
232
            $this->_row ++;//当前行数
233
        }
234
235
        $_merge = $this->cellName($this->_col);
236
        foreach ($this->fileTitle as $key => $val) {
237
            if(!empty($this->titleHeight)) {
238
                $this->workSheet->getRowDimension($this->_col)->setRowHeight($this->titleHeight);//行高度
239
            }
240
            $rowName = $this->cellName($this->_col);
241
            $this->workSheet->getStyle($rowName . $this->_row)->getAlignment()->setWrapText(true);//自动换行
242
            if (is_array($val)) {
243
                $num = 1;
244
                $_cols = $this->_col;
245
                foreach ($val as $k => $v) {
246
                    $this->workSheet->setCellValue($this->cellName($_cols) . ($this->_row+1), $k);
247
                    if(!empty($this->titleWidth)) {
248
                        $this->workSheet->getColumnDimension($this->cellName($_cols))->setWidth($this->titleWidth); //列宽度
249
                    }else{
250
                        $this->workSheet->getColumnDimension($this->cellName($_cols))->setAutoSize(true); //自动计算宽度
251
                    }
252
                    if ($num < count($val)) {
253
                        $this->_col++;
254
                        $num++;
255
                    }
256
                    $_cols++;
257
                }
258
                $this->workSheet->mergeCells($_merge . $this->_row.':' . $this->cellName($this->_col) .$this->_row);
259
                $this->workSheet->setCellValue($_merge . $this->_row, $key);//设置值
260
            } else {
261
                if ($this->title_row != 1) {
262
                    $this->workSheet->mergeCells($rowName . $this->_row.':' . $rowName . ($this->_row + $this->title_row - 1));
263
                }
264
                $this->workSheet->setCellValue($rowName . $this->_row, $key);//设置值
265
                if(!empty($this->titleWidth)){
266
                    $this->workSheet->getColumnDimension($rowName)->setWidth($this->titleWidth); //列宽度
267
                }else{
268
                    $this->workSheet->getColumnDimension($rowName)->setAutoSize(true); //自动计算宽度
269
                }
270
            }
271
            $this->_col++;
272
            $_merge = $this->cellName($this->_col);
273
        }
274
        $this->_row += $this->title_row;//当前行数
275
    }
276
277
    /**
278
     * excel单元格赋值
279
     * @author tinymeng
280
     * @param array $val 数据
281
     */
282
    private function excelSetCellValue($val)
283
    {
284
        //设置单元格行高
285
        if(!empty($this->height)){
286
            $this->workSheet->getRowDimension($this->_row)->setRowHeight($this->height);
287
        }
288
        $_lie = 0;
289
        foreach ($this->field as $v){
290
            $rowName = $this->cellName($_lie);
291
292
            if(strpos($v,'.') !== false){
293
                $v = explode('.',$v);
294
                $content = $val;
295
                for ($i=0;$i<count($v);$i++){
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
296
                    $content = $content[$v[$i]]??'';
297
                }
298
            }elseif($v == '_id'){
299
                $content = $this->_row-$this->title_row;//自增序号列
300
            }else{
301
                $content = ($val[$v]??'');
302
            }
303
            if(is_array($content) && isset($content['type']) && isset($content['content'])){
304
                if($content['type'] == 'image'){
305
                    $path = $this->verifyFile($content['content']);
0 ignored issues
show
Bug introduced by
It seems like verifyFile() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

305
                    /** @scrutinizer ignore-call */ 
306
                    $path = $this->verifyFile($content['content']);
Loading history...
306
                    $drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
307
                    $drawing->setPath($path);
308
                    if(!empty($content['height'])) {
309
                        $drawing->setHeight($content['height']);
310
                    }
311
                    if(!empty($content['width'])) {
312
                        $drawing->setWidth($content['width']);//只设置高,宽会自适应,如果设置宽后,高则失效
313
                    }
314
                    if(!empty($content['offsetX'])) {
315
                        $drawing->setOffsetX($content['offsetX']);//设置X方向偏移量
316
                    }
317
                    if(!empty($content['offsetY'])) {
318
                        $drawing->setOffsetY($content['offsetY']);//设置Y方向偏移量
319
                    }
320
321
                    $drawing->setCoordinates($rowName.$this->_row);
322
                    $drawing->setWorksheet($this->workSheet);
323
                }
324
            }else {
325
                $content = $this->formatValue($content);//格式化数据
0 ignored issues
show
Bug introduced by
It seems like formatValue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

325
                /** @scrutinizer ignore-call */ 
326
                $content = $this->formatValue($content);//格式化数据
Loading history...
326
                if (is_numeric($content)){
327
                    if($this->autoDataType && strlen($content)<11){
328
                        $this->workSheet->setCellValueExplicit($rowName.$this->_row, $content,DataType::TYPE_NUMERIC);
329
                    }else{
330
                        $this->workSheet->setCellValueExplicit($rowName.$this->_row, $content,DataType::TYPE_STRING2);
331
                    }
332
                }else{
333
                    $this->workSheet->setCellValueExplicit($rowName.$this->_row, $content,DataType::TYPE_STRING2);
334
                }
335
            }
336
            $_lie ++;
337
        }
338
        $this->_row ++;
339
    }
340
341
    /**
342
     * 单元格合并并赋值
343
     * @param array $data 数据
344
     * @param $group_left_count
345
     * @author tinymeng
346
     */
347
    private function excelGroupLeft(array $data, $group_left_count)
348
    {
349
        // 获取分组字段在field中的实际位置
350
        $group_field_positions = [];
351
        foreach($this->group_left as $group_field){
352
            $position = array_search($group_field, $this->field);
353
            if($position !== false){
354
                $group_field_positions[] = $position;
355
            }
356
        }
357
358
        if(empty($group_field_positions)){
359
            throw new TinymengException(StatusCode::COMMON_PARAM_INVALID, '分组字段未在标题中定义');
360
        }
361
362
        $group_start = $this->_row;
363
        foreach ($data as $key => $val){
364
            // 第一级分组的合并单元格
365
            $rowName = $this->cellName($group_field_positions[0]); // 使用第一个分组字段的实际位置
366
            $coordinate = $rowName.$this->_row.':'.$rowName.($this->_row+$val['count']-1);
367
            $this->workSheet->mergeCells($coordinate);
368
            $this->workSheet->setCellValue($rowName.$this->_row, $key);
369
            
370
            if($group_left_count == 1){
371
                foreach ($val['data'] as $data){
372
                    $this->excelSetCellValue($data);
373
                }
374
            }else{
375
                $sub_group_start = $this->_row;
376
                $rowName = $this->cellName($group_field_positions[1]); // 使用第二个分组字段的实际位置
377
                
378
                foreach ($val['data'] as $k => $v){
379
                    $coordinate = $rowName.$sub_group_start.':'.$rowName.($sub_group_start+$v['count']-1);
380
                    $this->workSheet->mergeCells($coordinate);
381
                    $this->workSheet->setCellValue($rowName.$sub_group_start, $k);
382
                    
383
                    foreach ($v['data'] as $data){
384
                        $this->excelSetCellValue($data);
385
                    }
386
                    
387
                    $sub_group_start = $sub_group_start + $v['count'];
388
                }
389
            }
390
            
391
            $this->_row = $group_start + $val['count'];
392
            $group_start = $this->_row;
393
        }
394
    }
395
396
    /**
397
     * 二位数组获取每一级别数量
398
     * @author tinymeng
399
     * @param array $data 二维数组原始数据
400
     * @return array
401
     */
402
    private function arrayCount($data=[])
403
    {
404
        foreach ($data as $key => $val){
405
            $num = 0;
406
            foreach ($val as $k => $v){
407
                $sub_num = count($v);
408
                $num = $num+$sub_num;
409
                $val[$k] = [
410
                    'count' => $sub_num,
411
                    'data' => $v
412
                ];
413
            }
414
            $data[$key] = [
415
                'count' => $num,
416
                'data' => $val
417
            ];
418
        }
419
        return $data;
420
    }
421
422
423
}
424