Passed
Branch master (eee465)
by ma
03:18
created

TWorkSheet::setWorkSheetData()   F

Complexity

Conditions 16
Paths 1920

Size

Total Lines 101
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 16
eloc 54
c 5
b 0
f 0
nc 1920
nop 2
dl 0
loc 101
rs 1.4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 tinymeng\spreadsheet\Excel\Handler\CellValueHandler;
11
use tinymeng\spreadsheet\Excel\Handler\DataValidationHandler;
12
use tinymeng\spreadsheet\Excel\Handler\GroupHandler;
13
use tinymeng\spreadsheet\Excel\Handler\HeaderHandler;
14
use tinymeng\spreadsheet\Excel\Handler\MergeHandler;
15
use tinymeng\spreadsheet\Excel\Handler\StyleHandler;
16
use tinymeng\spreadsheet\Util\ConstCode;
17
use tinymeng\tools\exception\StatusCode;
18
use tinymeng\tools\exception\TinymengException;
19
20
trait TWorkSheet{
21
22
    /**
23
     * sheet名称
24
     * @var
25
     */
26
    private $sheetName;
27
    /**
28
     * 查询数据
29
     * @var
30
     */
31
    private $data;
32
33
    /**
34
     * 定义默认列数
35
     * @var int
36
     */
37
    private $_col = 0;
38
    /**
39
     * 定义当前行数
40
     * @var int
41
     */
42
    private $_row = 1;
43
    /**
44
     * 定义所有字段
45
     * @var array
46
     */
47
    private $field = [];
48
49
    /**
50
     * 文件信息
51
     * @var array
52
     */
53
    private $fileTitle=[];
54
55
56
    /**
57
     * 左侧分组字段
58
     * @var array
59
     */
60
    private $group_left = [];
61
62
63
    /**
64
     * 获取sheet表格数目
65
     * @var
66
     */
67
    private $sheetCount = 1;
68
69
    /**
70
     * 字段映射方式
71
     * @var int
72
     */
73
    private $fieldMappingMethod = ConstCode::FIELD_MAPPING_METHOD_FIELD_CORRESPONDING_NAME;
74
75
    /**
76
     * 需要自动合并的字段
77
     * @var array
78
     */
79
    private $mergeColumns = [];
80
81
    /**
82
     * 小计行样式
83
     * @var array
84
     */
85
    private $subtotalStyle = [];
86
    /**
87
     * 全表样式
88
     * @var array
89
     */
90
    private $sheetStyle = [];
91
92
    /**
93
     * 用户自定义表格操作回调
94
     * @var callable|null
95
     */
96
    private $complexFormatCallback = null;
97
    /**
98
     * @var array
99
     */
100
    private $titleConfig = [];
101
102
    /**
103
     * 列的数据验证配置
104
     * @var array 格式:['field_name' => ['type' => 'list', 'options' => [...], 'promptTitle' => '', 'promptMessage' => '', ...]]
105
     */
106
    private $columnValidations = [];
107
108
    /**
109
     * 必填字段列表
110
     * @var array 格式:['field_name1', 'field_name2', ...] 或 在 titleConfig 中通过 'required_fields' 配置
111
     */
112
    private $requiredFields = [];
113
114
    /**
115
     * @param $data
116
     * @return $this
117
     */
118
    public function setData($data){
119
        $this->data = $data;
120
        return $this;
121
    }
122
123
    /**
124
     * @param $data
125
     * @return $this
126
     */
127
    public function getData(){
128
        return $this->data;
129
    }
130
131
132
    /**
133
     * @return void
134
     * @throws \PhpOffice\PhpSpreadsheet\Exception
135
     */
136
    public function initWorkSheet()
137
    {
138
        $this->_col = 0;
139
        $this->_row = 1;
140
        $this->fileTitle = [];
141
        $this->data = [];
142
        $this->field = [];
143
        $this->columnValidations = [];
144
        if($this->freezePane) $this->workSheet->freezePane($this->freezePane); //冻结窗格
145
    }
146
147
    /**
148
     * @param $fileTitle
149
     * @param $data
150
     * @return $this
151
     * @throws TinymengException
152
     * @throws \PhpOffice\PhpSpreadsheet\Exception
153
     */
154
    public function setWorkSheetData($titleConfig,$data)
155
    {
156
        $this->titleConfig = $titleConfig;
157
        if(isset($titleConfig['title_row']) || isset($titleConfig['group_left'])){
158
            /**
159
             * $titleConfig = [
160
             *       'title_row'=>1,
161
             *       'group_left'=>[],
162
             *       'title'=>[
163
             *           '姓名'=>'name'
164
             *       ],
165
             *  ];
166
             */
167
            $this->title_row = $titleConfig['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...
168
            $this->group_left = $titleConfig['group_left']??[];
169
            $titleData = $titleConfig['title']??[];
170
            // 新增:读取mergeColumns配置
171
            if (isset($titleConfig['mergeColumns'])) {
172
                $this->mergeColumns = $titleConfig['mergeColumns'];
173
            }
174
            // 新增:读取必填字段配置
175
            if (isset($titleConfig['required_fields'])) {
176
                $this->requiredFields = $titleConfig['required_fields'];
177
            }
178
        }else{
179
            /**
180
             *  $titleConfig = [
181
             *       '姓名'=>'name',
182
             *  ];
183
             */
184
            $titleData = $titleConfig;
185
        }
186
        // 根据字段映射方式处理 title
187
        if ($this->fieldMappingMethod === ConstCode::FIELD_MAPPING_METHOD_FIELD_CORRESPONDING_NAME) {
188
            $this->fileTitle = array_flip($titleData);// 字段对应名称方式 - 需要将键值对调
189
        }else{
190
            $this->fileTitle = $titleData;// 名称对应字段方式 - 保持原样
191
        }
192
        $this->data = $data;
193
194
        /** 设置第一行格式 */
195
        if(!empty($this->mainTitle)){
196
            HeaderHandler::setHeader($this->workSheet, $this->mainTitle, $this->fileTitle);
197
            $this->_row++; // 当前行数
198
        }
199
200
        /** 设置表头 **/
201
        $result = HeaderHandler::setTitle(
202
            $this->workSheet,
203
            $this->fileTitle,
204
            $this->title_row ?? 1,
205
            $this->titleConfig,
206
            $this->_col,
207
            $this->_row,
208
            $this->titleHeight ?? null,
209
            $this->titleWidth ?? null,
210
            $this->requiredFields
211
        );
212
        $this->_col = $result['col'];
213
        $this->_row = $result['row'];
214
215
        /** 获取列表里所有字段 **/
216
        foreach ($this->fileTitle as $key => $val){
217
            if(is_array($val)){
218
                foreach ($val as $k => $v){
219
                    $this->field[] = $v;
220
                }
221
            }else{
222
                $this->field[] = $val;
223
            }
224
        }
225
        /** 查询结果赋值 **/
226
        if(!empty($this->data)){
227
            $this->excelSetValue();
228
        }
229
        // 读取样式配置
230
        if (!empty($this->config['subtotalStyle'])) {
231
            $this->subtotalStyle = $this->config['subtotalStyle'];
232
        }
233
        if (!empty($this->config['sheetStyle'])) {
234
            $this->sheetStyle = $this->config['sheetStyle'];
235
        }
236
237
        // 新增:应用全表样式
238
        StyleHandler::applySheetStyle(
239
            $this->workSheet,
240
            $this->sheetStyle,
241
            $this->field,
242
            $this->_row - 1
243
        );
244
        // 新增:应用数据验证(如果没有数据也要应用,用于导出模板)
245
        if (!empty($this->columnValidations) && empty($this->data)) {
246
            // 确定数据起始行
247
            $dataStartRow = $this->titleConfig['data_start_row'] ?? $this->_row;
248
            $this->applyAllColumnValidations($dataStartRow, 0);
249
        }
250
        // 新增:调用自定义表格操作回调
251
        if (is_callable($this->complexFormatCallback)) {
252
            call_user_func($this->complexFormatCallback, $this->workSheet);
0 ignored issues
show
Bug introduced by
It seems like $this->complexFormatCallback can also be of type null; however, parameter $callback of call_user_func() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

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

252
            call_user_func(/** @scrutinizer ignore-type */ $this->complexFormatCallback, $this->workSheet);
Loading history...
253
        }
254
        return $this;
255
    }
256
257
    /**
258
     * excelSetValue
259
     * @author: Tinymeng <[email protected]>
260
     * @time: 2022/2/22 11:43
261
     */
262
    public function excelSetValue(){
263
        if(!empty($this->titleConfig['data_start_row'])){
264
            $this->_row = $this->titleConfig['data_start_row'];
265
        }
266
267
        if(empty($this->group_left)){ //判断左侧是否分组
268
            $rowStart = $this->_row;
269
            foreach ($this->data as $key => $val){
270
                $this->excelSetCellValue($val);
271
            }
272
            // 新增:处理mergeColumns自动合并
273
            if (!empty($this->mergeColumns)) {
274
                MergeHandler::autoMergeColumns(
275
                    $this->workSheet,
276
                    $this->mergeColumns,
277
                    $this->field,
278
                    $rowStart,
279
                    $this->_row - 1
280
                );
281
            }
282
            // 新增:应用数据验证(无分组情况)
283
            if (!empty($this->columnValidations)) {
284
                $this->applyAllColumnValidations($rowStart, $this->_row - 1);
285
            }
286
        }else{   //根据设置分组字段进行分组
287
            /** 数据分组 **/
288
            $group_left_count = count($this->group_left);
289
            if($group_left_count == 1){
290
                $data = GroupHandler::groupDataByOneField($this->data, $this->group_left[0]);
291
                $rowStart = $this->_row;
292
                $this->_row = GroupHandler::processGroupLeft(
293
                    $this->workSheet,
294
                    $data,
295
                    $group_left_count,
296
                    $this->group_left,
297
                    $this->field,
298
                    $this->mergeColumns,
299
                    $this->_row,
300
                    function($val) {
301
                        return $this->excelSetCellValue($val);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->excelSetCellValue($val) targeting tinymeng\spreadsheet\Exc...et::excelSetCellValue() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
302
                    }
303
                );
304
                // 新增:应用数据验证(分组情况1级)
305
                if (!empty($this->columnValidations)) {
306
                    $this->applyAllColumnValidations($rowStart, $this->_row - 1);
307
                }
308
            }elseif($group_left_count == 2){
309
                $this->data = GroupHandler::groupDataByTwoFields(
310
                    $this->data,
311
                    $this->group_left[0],
312
                    $this->group_left[1]
313
                );
314
                $rowStart = $this->_row;
315
                $this->_row = GroupHandler::processGroupLeft(
316
                    $this->workSheet,
317
                    $this->data,
318
                    $group_left_count,
319
                    $this->group_left,
320
                    $this->field,
321
                    $this->mergeColumns,
322
                    $this->_row,
323
                    function($val) {
324
                        return $this->excelSetCellValue($val);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->excelSetCellValue($val) targeting tinymeng\spreadsheet\Exc...et::excelSetCellValue() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
325
                    }
326
                );
327
                // 新增:应用数据验证(分组情况2级)
328
                if (!empty($this->columnValidations)) {
329
                    $this->applyAllColumnValidations($rowStart, $this->_row - 1);
330
                }
331
            }else{
332
                throw new TinymengException(StatusCode::COMMON_PARAM_INVALID,
333
                    '左侧分组过多,导出失败!'
334
                );
335
            }
336
        }
337
    }
338
339
340
    /**
341
     * excel单元格赋值
342
     * @author tinymeng
343
     * @param array $val 数据
344
     */
345
    private function excelSetCellValue($val)
346
    {
347
        $this->_row = CellValueHandler::setCellValue(
348
            $this->workSheet,
349
            $val,
350
            $this->field,
351
            $this->_row,
352
            $this->title_row ?? 1,
353
            $this->height ?? null,
354
            $this->autoDataType ?? false,
355
            $this->format ?? true,
356
            $this->format_date ?? 'Y-m-d H:i:s'
357
        );
358
    }
359
360
361
    /**
362
     * 设置自定义表格操作回调
363
     * @param callable $fn
364
     * @return $this
365
     */
366
    public function complexFormat(callable $fn) {
367
        $this->complexFormatCallback = $fn;
368
        return $this;
369
    }
370
371
    /**
372
     * 设置列的数据验证和输入提示
373
     * @param string $fieldName 字段名(对应 $fileTitle 中的字段)
374
     * @param array $config 验证配置
375
     *   格式:
376
     *   [
377
     *     'type' => 'list',           // 验证类型: list(下拉列表), whole(整数), decimal(小数), date(日期), time(时间), textLength(文本长度), custom(自定义公式)
378
     *     'options' => ['选项1', '选项2'], // 当type为list时的选项列表
379
     *     'formula' => 'A1:A10',      // 当type为custom或list需要引用范围时的公式
380
     *     'operator' => 'between',    // 操作符: between, notBetween, equal, notEqual, greaterThan, lessThan, greaterThanOrEqual, lessThanOrEqual
381
     *     'min' => 0,                // 最小值(用于whole, decimal, date, time, textLength)
382
     *     'max' => 100,              // 最大值(用于whole, decimal, date, time, textLength)
383
     *     'promptTitle' => '输入提示',   // 输入提示标题
384
     *     'promptMessage' => '请输入...', // 输入提示内容
385
     *     'errorTitle' => '输入错误',    // 错误提示标题
386
     *     'errorMessage' => '输入值无效',  // 错误提示内容
387
     *     'errorStyle' => 'stop',     // 错误样式: stop(停止), warning(警告), information(信息)
388
     *     'showInputMessage' => true, // 是否显示输入提示
389
     *     'showErrorMessage' => true,  // 是否显示错误提示
390
     *     'allowBlank' => false,       // 是否允许空白
391
     *     'showDropDown' => true,      // 是否显示下拉箭头(仅list类型)
392
     *     'data_end_row' => 200,       // 数据结束行(模板导出时使用,为0或未设置时应用到整列)
393
     *     'data_row_count' => 100,     // 数据行数(模板导出时使用,从数据起始行开始计算的行数)
394
     *   ]
395
     * @return $this
396
     */
397
    public function setColumnValidation(string $fieldName, array $config) {
398
        $this->columnValidations[$fieldName] = $config;
399
        return $this;
400
    }
401
402
    /**
403
     * 批量设置列的数据验证
404
     * @param array $validations 格式:['field_name' => [验证配置], ...]
405
     * @return $this
406
     */
407
    public function setColumnValidations(array $validations) {
408
        foreach ($validations as $fieldName => $config) {
409
            $this->setColumnValidation($fieldName, $config);
410
        }
411
        return $this;
412
    }
413
414
    /**
415
     * 应用数据验证到指定列
416
     * @param string $fieldName 字段名
417
     * @param int $startRow 起始行(数据开始行)
418
     * @param int $endRow 结束行(数据结束行,为0时表示应用到整列)
419
     */
420
    private function applyColumnValidation(string $fieldName, int $startRow, int $endRow = 0) {
421
        if (!isset($this->columnValidations[$fieldName])) {
422
            return;
423
        }
424
425
        // 查找字段在 field 数组中的位置
426
        $fieldIndex = array_search($fieldName, $this->field);
427
        if ($fieldIndex === false) {
428
            return;
429
        }
430
431
        $config = $this->columnValidations[$fieldName];
432
433
        // 使用 DataValidationHandler 处理验证
434
        DataValidationHandler::applyValidation(
435
            $this->workSheet,
436
            $fieldName,
437
            $config,
438
            $fieldIndex,
0 ignored issues
show
Bug introduced by
It seems like $fieldIndex can also be of type string; however, parameter $fieldIndex of tinymeng\spreadsheet\Exc...dler::applyValidation() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

438
            /** @scrutinizer ignore-type */ $fieldIndex,
Loading history...
439
            $startRow,
440
            $endRow,
441
            empty($this->data)
442
        );
443
    }
444
445
    /**
446
     * 应用所有列的数据验证
447
     * @param int $dataStartRow 数据开始行
448
     * @param int $dataEndRow 数据结束行(为0时表示应用到整列)
449
     */
450
    private function applyAllColumnValidations(int $dataStartRow, int $dataEndRow = 0) {
451
        foreach ($this->columnValidations as $fieldName => $config) {
452
            $this->applyColumnValidation($fieldName, $dataStartRow, $dataEndRow);
453
        }
454
    }
455
456
    /**
457
     * 设置必填字段
458
     * @param array $fields 必填字段列表,格式:['field_name1', 'field_name2', ...]
459
     * @return $this
460
     */
461
    public function setRequiredFields(array $fields) {
462
        $this->requiredFields = $fields;
463
        return $this;
464
    }
465
466
    /**
467
     * 获取必填字段列表
468
     * @return array
469
     */
470
    public function getRequiredFields(): array {
471
        return $this->requiredFields;
472
    }
473
474
}
475