1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PhpOffice\PhpSpreadsheet\Worksheet; |
4
|
|
|
|
5
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\AddressRange; |
6
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\CellAddress; |
7
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; |
8
|
|
|
use PhpOffice\PhpSpreadsheet\Cell\DataType; |
9
|
|
|
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException; |
10
|
|
|
use PhpOffice\PhpSpreadsheet\Shared\StringHelper; |
11
|
|
|
use PhpOffice\PhpSpreadsheet\Spreadsheet; |
12
|
|
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle; |
13
|
|
|
use Stringable; |
14
|
|
|
|
15
|
|
|
class Table implements Stringable |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* Table Name. |
19
|
|
|
*/ |
20
|
|
|
private string $name; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Show Header Row. |
24
|
|
|
*/ |
25
|
|
|
private bool $showHeaderRow = true; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Show Totals Row. |
29
|
|
|
*/ |
30
|
|
|
private bool $showTotalsRow = false; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Table Range. |
34
|
|
|
*/ |
35
|
|
|
private string $range = ''; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* Table Worksheet. |
39
|
|
|
*/ |
40
|
|
|
private ?Worksheet $workSheet = null; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Table allow filter. |
44
|
|
|
*/ |
45
|
|
|
private bool $allowFilter = true; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Table Column. |
49
|
|
|
* |
50
|
|
|
* @var Table\Column[] |
51
|
|
|
*/ |
52
|
|
|
private array $columns = []; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Table Style. |
56
|
|
|
*/ |
57
|
|
|
private TableStyle $style; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Table AutoFilter. |
61
|
|
|
*/ |
62
|
|
|
private AutoFilter $autoFilter; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Create a new Table. |
66
|
|
|
* |
67
|
|
|
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range |
68
|
|
|
* A simple string containing a Cell range like 'A1:E10' is permitted |
69
|
|
|
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]), |
70
|
|
|
* or an AddressRange object. |
71
|
|
|
* @param string $name (e.g. Table1) |
72
|
|
|
*/ |
73
|
156 |
|
public function __construct(AddressRange|string|array $range = '', string $name = '') |
74
|
|
|
{ |
75
|
156 |
|
$this->style = new TableStyle(); |
76
|
156 |
|
$this->autoFilter = new AutoFilter($range); |
77
|
156 |
|
$this->setRange($range); |
78
|
152 |
|
$this->setName($name); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Code to execute when this table is unset(). |
83
|
|
|
*/ |
84
|
51 |
|
public function __destruct() |
85
|
|
|
{ |
86
|
51 |
|
$this->workSheet = null; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Get Table name. |
91
|
|
|
*/ |
92
|
87 |
|
public function getName(): string |
93
|
|
|
{ |
94
|
87 |
|
return $this->name; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Set Table name. |
99
|
|
|
* |
100
|
|
|
* @throws PhpSpreadsheetException |
101
|
|
|
*/ |
102
|
152 |
|
public function setName(string $name): self |
103
|
|
|
{ |
104
|
152 |
|
$name = trim($name); |
105
|
|
|
|
106
|
152 |
|
if (!empty($name)) { |
107
|
115 |
|
if (strlen($name) === 1 && in_array($name, ['C', 'c', 'R', 'r'])) { |
108
|
4 |
|
throw new PhpSpreadsheetException('The table name is invalid'); |
109
|
|
|
} |
110
|
111 |
|
if (StringHelper::countCharacters($name) > 255) { |
111
|
1 |
|
throw new PhpSpreadsheetException('The table name cannot be longer than 255 characters'); |
112
|
|
|
} |
113
|
|
|
// Check for A1 or R1C1 cell reference notation |
114
|
|
|
if ( |
115
|
110 |
|
preg_match(Coordinate::A1_COORDINATE_REGEX, $name) |
116
|
110 |
|
|| preg_match('/^R\[?\-?[0-9]*\]?C\[?\-?[0-9]*\]?$/i', $name) |
117
|
|
|
) { |
118
|
5 |
|
throw new PhpSpreadsheetException('The table name can\'t be the same as a cell reference'); |
119
|
|
|
} |
120
|
105 |
|
if (!preg_match('/^[\p{L}_\\\]/iu', $name)) { |
121
|
3 |
|
throw new PhpSpreadsheetException('The table name must begin a name with a letter, an underscore character (_), or a backslash (\)'); |
122
|
|
|
} |
123
|
102 |
|
if (!preg_match('/^[\p{L}_\\\][\p{L}\p{M}0-9\._]+$/iu', $name)) { |
124
|
1 |
|
throw new PhpSpreadsheetException('The table name contains invalid characters'); |
125
|
|
|
} |
126
|
|
|
|
127
|
101 |
|
$this->checkForDuplicateTableNames($name, $this->workSheet); |
128
|
101 |
|
$this->updateStructuredReferences($name); |
129
|
|
|
} |
130
|
|
|
|
131
|
152 |
|
$this->name = $name; |
132
|
|
|
|
133
|
152 |
|
return $this; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* @throws PhpSpreadsheetException |
138
|
|
|
*/ |
139
|
101 |
|
private function checkForDuplicateTableNames(string $name, ?Worksheet $worksheet): void |
140
|
|
|
{ |
141
|
|
|
// Remember that table names are case-insensitive |
142
|
101 |
|
$tableName = StringHelper::strToLower($name); |
143
|
|
|
|
144
|
101 |
|
if ($worksheet !== null && StringHelper::strToLower($this->name) !== $name) { |
145
|
3 |
|
$spreadsheet = $worksheet->getParentOrThrow(); |
146
|
|
|
|
147
|
3 |
|
foreach ($spreadsheet->getWorksheetIterator() as $sheet) { |
148
|
3 |
|
foreach ($sheet->getTableCollection() as $table) { |
149
|
3 |
|
if (StringHelper::strToLower($table->getName()) === $tableName && $table != $this) { |
150
|
1 |
|
throw new PhpSpreadsheetException("Spreadsheet already contains a table named '{$this->name}'"); |
151
|
|
|
} |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
101 |
|
private function updateStructuredReferences(string $name): void |
158
|
|
|
{ |
159
|
101 |
|
if (!$this->workSheet || !$this->name) { |
160
|
101 |
|
return; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// Remember that table names are case-insensitive |
164
|
2 |
|
if (StringHelper::strToLower($this->name) !== StringHelper::strToLower($name)) { |
165
|
|
|
// We need to check all formula cells that might contain fully-qualified Structured References |
166
|
|
|
// that refer to this table, and update those formulae to reference the new table name |
167
|
2 |
|
$spreadsheet = $this->workSheet->getParentOrThrow(); |
168
|
2 |
|
foreach ($spreadsheet->getWorksheetIterator() as $sheet) { |
169
|
2 |
|
$this->updateStructuredReferencesInCells($sheet, $name); |
170
|
|
|
} |
171
|
2 |
|
$this->updateStructuredReferencesInNamedFormulae($spreadsheet, $name); |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
175
|
2 |
|
private function updateStructuredReferencesInCells(Worksheet $worksheet, string $newName): void |
176
|
|
|
{ |
177
|
2 |
|
$pattern = '/' . preg_quote($this->name, '/') . '\[/mui'; |
178
|
|
|
|
179
|
2 |
|
foreach ($worksheet->getCoordinates(false) as $coordinate) { |
180
|
2 |
|
$cell = $worksheet->getCell($coordinate); |
181
|
2 |
|
if ($cell->getDataType() === DataType::TYPE_FORMULA) { |
182
|
2 |
|
$formula = $cell->getValueString(); |
183
|
2 |
|
if (preg_match($pattern, $formula) === 1) { |
184
|
2 |
|
$formula = preg_replace($pattern, "{$newName}[", $formula); |
185
|
2 |
|
$cell->setValueExplicit($formula, DataType::TYPE_FORMULA); |
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
2 |
|
private function updateStructuredReferencesInNamedFormulae(Spreadsheet $spreadsheet, string $newName): void |
192
|
|
|
{ |
193
|
2 |
|
$pattern = '/' . preg_quote($this->name, '/') . '\[/mui'; |
194
|
|
|
|
195
|
2 |
|
foreach ($spreadsheet->getNamedFormulae() as $namedFormula) { |
196
|
2 |
|
$formula = $namedFormula->getValue(); |
197
|
2 |
|
if (preg_match($pattern, $formula) === 1) { |
198
|
2 |
|
$formula = preg_replace($pattern, "{$newName}[", $formula) ?? ''; |
199
|
2 |
|
$namedFormula->setValue($formula); |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Get show Header Row. |
206
|
|
|
*/ |
207
|
71 |
|
public function getShowHeaderRow(): bool |
208
|
|
|
{ |
209
|
71 |
|
return $this->showHeaderRow; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Set show Header Row. |
214
|
|
|
*/ |
215
|
36 |
|
public function setShowHeaderRow(bool $showHeaderRow): self |
216
|
|
|
{ |
217
|
36 |
|
$this->showHeaderRow = $showHeaderRow; |
218
|
|
|
|
219
|
36 |
|
return $this; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Get show Totals Row. |
224
|
|
|
*/ |
225
|
86 |
|
public function getShowTotalsRow(): bool |
226
|
|
|
{ |
227
|
86 |
|
return $this->showTotalsRow; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Set show Totals Row. |
232
|
|
|
*/ |
233
|
82 |
|
public function setShowTotalsRow(bool $showTotalsRow): self |
234
|
|
|
{ |
235
|
82 |
|
$this->showTotalsRow = $showTotalsRow; |
236
|
|
|
|
237
|
82 |
|
return $this; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Get allow filter. |
242
|
|
|
* If false, autofiltering is disabled for the table, if true it is enabled. |
243
|
|
|
*/ |
244
|
13 |
|
public function getAllowFilter(): bool |
245
|
|
|
{ |
246
|
13 |
|
return $this->allowFilter; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* Set show Autofiltering. |
251
|
|
|
* Disabling autofiltering has the same effect as hiding the filter button on all the columns in the table. |
252
|
|
|
*/ |
253
|
7 |
|
public function setAllowFilter(bool $allowFilter): self |
254
|
|
|
{ |
255
|
7 |
|
$this->allowFilter = $allowFilter; |
256
|
|
|
|
257
|
7 |
|
return $this; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Get Table Range. |
262
|
|
|
*/ |
263
|
92 |
|
public function getRange(): string |
264
|
|
|
{ |
265
|
92 |
|
return $this->range; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* Set Table Cell Range. |
270
|
|
|
* |
271
|
|
|
* @param AddressRange<CellAddress>|AddressRange<int>|AddressRange<string>|array{0: int, 1: int, 2: int, 3: int}|array{0: int, 1: int}|string $range |
272
|
|
|
* A simple string containing a Cell range like 'A1:E10' is permitted |
273
|
|
|
* or passing in an array of [$fromColumnIndex, $fromRow, $toColumnIndex, $toRow] (e.g. [3, 5, 6, 8]), |
274
|
|
|
* or an AddressRange object. |
275
|
|
|
*/ |
276
|
156 |
|
public function setRange(AddressRange|string|array $range = ''): self |
277
|
|
|
{ |
278
|
|
|
// extract coordinate |
279
|
156 |
|
if ($range !== '') { |
280
|
152 |
|
[, $range] = Worksheet::extractSheetTitle(Validations::validateCellRange($range), true); |
281
|
|
|
} |
282
|
156 |
|
if (empty($range)) { |
283
|
|
|
// Discard all column rules |
284
|
14 |
|
$this->columns = []; |
285
|
14 |
|
$this->range = ''; |
286
|
|
|
|
287
|
14 |
|
return $this; |
288
|
|
|
} |
289
|
|
|
|
290
|
152 |
|
if (!str_contains($range, ':')) { |
291
|
1 |
|
throw new PhpSpreadsheetException('Table must be set on a range of cells.'); |
292
|
|
|
} |
293
|
|
|
|
294
|
151 |
|
[$width, $height] = Coordinate::rangeDimension($range); |
295
|
151 |
|
if ($width < 1 || $height < 1) { |
296
|
3 |
|
throw new PhpSpreadsheetException('The table range must be at least 1 column and row'); |
297
|
|
|
} |
298
|
|
|
|
299
|
148 |
|
$this->range = $range; |
300
|
148 |
|
$this->autoFilter->setRange($range); |
301
|
|
|
|
302
|
|
|
// Discard any column rules that are no longer valid within this range |
303
|
148 |
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); |
304
|
148 |
|
foreach ($this->columns as $key => $value) { |
305
|
2 |
|
$colIndex = Coordinate::columnIndexFromString($key); |
306
|
2 |
|
if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) { |
307
|
1 |
|
unset($this->columns[$key]); |
308
|
|
|
} |
309
|
|
|
} |
310
|
|
|
|
311
|
148 |
|
return $this; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* Set Table Cell Range to max row. |
316
|
|
|
*/ |
317
|
|
|
public function setRangeToMaxRow(): self |
318
|
|
|
{ |
319
|
|
|
if ($this->workSheet !== null) { |
320
|
|
|
$thisrange = $this->range; |
321
|
|
|
$range = (string) preg_replace('/\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange); |
322
|
|
|
if ($range !== $thisrange) { |
323
|
|
|
$this->setRange($range); |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
return $this; |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Get Table's Worksheet. |
332
|
|
|
*/ |
333
|
70 |
|
public function getWorksheet(): ?Worksheet |
334
|
|
|
{ |
335
|
70 |
|
return $this->workSheet; |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Set Table's Worksheet. |
340
|
|
|
*/ |
341
|
103 |
|
public function setWorksheet(?Worksheet $worksheet = null): self |
342
|
|
|
{ |
343
|
103 |
|
if ($this->name !== '' && $worksheet !== null) { |
344
|
95 |
|
$spreadsheet = $worksheet->getParentOrThrow(); |
345
|
95 |
|
$tableName = StringHelper::strToUpper($this->name); |
346
|
|
|
|
347
|
95 |
|
foreach ($spreadsheet->getWorksheetIterator() as $sheet) { |
348
|
95 |
|
foreach ($sheet->getTableCollection() as $table) { |
349
|
6 |
|
if (StringHelper::strToUpper($table->getName()) === $tableName) { |
350
|
1 |
|
throw new PhpSpreadsheetException("Workbook already contains a table named '{$this->name}'"); |
351
|
|
|
} |
352
|
|
|
} |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
|
356
|
103 |
|
$this->workSheet = $worksheet; |
357
|
103 |
|
$this->autoFilter->setParent($worksheet); |
358
|
|
|
|
359
|
103 |
|
return $this; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Get all Table Columns. |
364
|
|
|
* |
365
|
|
|
* @return Table\Column[] |
366
|
|
|
*/ |
367
|
10 |
|
public function getColumns(): array |
368
|
|
|
{ |
369
|
10 |
|
return $this->columns; |
370
|
|
|
} |
371
|
|
|
|
372
|
|
|
/** |
373
|
|
|
* Validate that the specified column is in the Table range. |
374
|
|
|
* |
375
|
|
|
* @param string $column Column name (e.g. A) |
376
|
|
|
* |
377
|
|
|
* @return int The column offset within the table range |
378
|
|
|
*/ |
379
|
104 |
|
public function isColumnInRange(string $column): int |
380
|
|
|
{ |
381
|
104 |
|
if (empty($this->range)) { |
382
|
2 |
|
throw new PhpSpreadsheetException('No table range is defined.'); |
383
|
|
|
} |
384
|
|
|
|
385
|
102 |
|
$columnIndex = Coordinate::columnIndexFromString($column); |
386
|
102 |
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); |
387
|
102 |
|
if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) { |
388
|
3 |
|
throw new PhpSpreadsheetException('Column is outside of current table range.'); |
389
|
|
|
} |
390
|
|
|
|
391
|
99 |
|
return $columnIndex - $rangeStart[0]; |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
/** |
395
|
|
|
* Get a specified Table Column Offset within the defined Table range. |
396
|
|
|
* |
397
|
|
|
* @param string $column Column name (e.g. A) |
398
|
|
|
* |
399
|
|
|
* @return int The offset of the specified column within the table range |
400
|
|
|
*/ |
401
|
2 |
|
public function getColumnOffset(string $column): int |
402
|
|
|
{ |
403
|
2 |
|
return $this->isColumnInRange($column); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Get a specified Table Column. |
408
|
|
|
* |
409
|
|
|
* @param string $column Column name (e.g. A) |
410
|
|
|
*/ |
411
|
93 |
|
public function getColumn(string $column): Table\Column |
412
|
|
|
{ |
413
|
93 |
|
$this->isColumnInRange($column); |
414
|
|
|
|
415
|
91 |
|
if (!isset($this->columns[$column])) { |
416
|
90 |
|
$this->columns[$column] = new Table\Column($column, $this); |
417
|
|
|
} |
418
|
|
|
|
419
|
91 |
|
return $this->columns[$column]; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Get a specified Table Column by it's offset. |
424
|
|
|
* |
425
|
|
|
* @param int $columnOffset Column offset within range (starting from 0) |
426
|
|
|
*/ |
427
|
38 |
|
public function getColumnByOffset(int $columnOffset): Table\Column |
428
|
|
|
{ |
429
|
38 |
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range); |
430
|
38 |
|
$pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $columnOffset); |
431
|
|
|
|
432
|
38 |
|
return $this->getColumn($pColumn); |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* Set Table. |
437
|
|
|
* |
438
|
|
|
* @param string|Table\Column $columnObjectOrString |
439
|
|
|
* A simple string containing a Column ID like 'A' is permitted |
440
|
|
|
*/ |
441
|
10 |
|
public function setColumn(string|Table\Column $columnObjectOrString): self |
442
|
|
|
{ |
443
|
10 |
|
if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) { |
444
|
9 |
|
$column = $columnObjectOrString; |
445
|
1 |
|
} elseif ($columnObjectOrString instanceof Table\Column) { |
446
|
1 |
|
$column = $columnObjectOrString->getColumnIndex(); |
447
|
|
|
} else { |
448
|
|
|
throw new PhpSpreadsheetException('Column is not within the table range.'); |
449
|
|
|
} |
450
|
10 |
|
$this->isColumnInRange($column); |
451
|
|
|
|
452
|
8 |
|
if (is_string($columnObjectOrString)) { |
453
|
7 |
|
$this->columns[$columnObjectOrString] = new Table\Column($columnObjectOrString, $this); |
454
|
|
|
} else { |
455
|
1 |
|
$columnObjectOrString->setTable($this); |
456
|
1 |
|
$this->columns[$column] = $columnObjectOrString; |
457
|
|
|
} |
458
|
8 |
|
ksort($this->columns); |
459
|
|
|
|
460
|
8 |
|
return $this; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Clear a specified Table Column. |
465
|
|
|
* |
466
|
|
|
* @param string $column Column name (e.g. A) |
467
|
|
|
*/ |
468
|
2 |
|
public function clearColumn(string $column): self |
469
|
|
|
{ |
470
|
2 |
|
$this->isColumnInRange($column); |
471
|
|
|
|
472
|
2 |
|
if (isset($this->columns[$column])) { |
473
|
2 |
|
unset($this->columns[$column]); |
474
|
|
|
} |
475
|
|
|
|
476
|
2 |
|
return $this; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
/** |
480
|
|
|
* Shift an Table Column Rule to a different column. |
481
|
|
|
* |
482
|
|
|
* Note: This method bypasses validation of the destination column to ensure it is within this Table range. |
483
|
|
|
* Nor does it verify whether any column rule already exists at $toColumn, but will simply override any existing value. |
484
|
|
|
* Use with caution. |
485
|
|
|
* |
486
|
|
|
* @param string $fromColumn Column name (e.g. A) |
487
|
|
|
* @param string $toColumn Column name (e.g. B) |
488
|
|
|
*/ |
489
|
2 |
|
public function shiftColumn(string $fromColumn, string $toColumn): self |
490
|
|
|
{ |
491
|
2 |
|
$fromColumn = strtoupper($fromColumn); |
492
|
2 |
|
$toColumn = strtoupper($toColumn); |
493
|
|
|
|
494
|
2 |
|
if (isset($this->columns[$fromColumn])) { |
495
|
1 |
|
$this->columns[$fromColumn]->setTable(); |
496
|
1 |
|
$this->columns[$fromColumn]->setColumnIndex($toColumn); |
497
|
1 |
|
$this->columns[$toColumn] = $this->columns[$fromColumn]; |
498
|
1 |
|
$this->columns[$toColumn]->setTable($this); |
499
|
1 |
|
unset($this->columns[$fromColumn]); |
500
|
|
|
|
501
|
1 |
|
ksort($this->columns); |
502
|
|
|
} |
503
|
|
|
|
504
|
2 |
|
return $this; |
505
|
|
|
} |
506
|
|
|
|
507
|
|
|
/** |
508
|
|
|
* Get table Style. |
509
|
|
|
*/ |
510
|
14 |
|
public function getStyle(): TableStyle |
511
|
|
|
{ |
512
|
14 |
|
return $this->style; |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
/** |
516
|
|
|
* Set table Style. |
517
|
|
|
*/ |
518
|
37 |
|
public function setStyle(TableStyle $style): self |
519
|
|
|
{ |
520
|
37 |
|
$this->style = $style; |
521
|
|
|
|
522
|
37 |
|
return $this; |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Get AutoFilter. |
527
|
|
|
*/ |
528
|
34 |
|
public function getAutoFilter(): AutoFilter |
529
|
|
|
{ |
530
|
34 |
|
return $this->autoFilter; |
531
|
|
|
} |
532
|
|
|
|
533
|
|
|
/** |
534
|
|
|
* Set AutoFilter. |
535
|
|
|
*/ |
536
|
|
|
public function setAutoFilter(AutoFilter $autoFilter): self |
537
|
|
|
{ |
538
|
|
|
$this->autoFilter = $autoFilter; |
539
|
|
|
|
540
|
|
|
return $this; |
541
|
|
|
} |
542
|
|
|
|
543
|
|
|
/** |
544
|
|
|
* Get the row number on this table for given coordinates. |
545
|
|
|
*/ |
546
|
4 |
|
public function getRowNumber(string $coordinate): int |
547
|
|
|
{ |
548
|
4 |
|
$range = $this->getRange(); |
549
|
4 |
|
$coords = Coordinate::splitRange($range); |
550
|
4 |
|
$firstCell = Coordinate::coordinateFromString($coords[0][0]); |
551
|
4 |
|
$thisCell = Coordinate::coordinateFromString($coordinate); |
552
|
|
|
|
553
|
4 |
|
return (int) $thisCell[1] - (int) $firstCell[1]; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* Implement PHP __clone to create a deep clone, not just a shallow copy. |
558
|
|
|
*/ |
559
|
2 |
|
public function __clone() |
560
|
|
|
{ |
561
|
2 |
|
$vars = get_object_vars($this); |
562
|
2 |
|
foreach ($vars as $key => $value) { |
563
|
2 |
|
if (is_object($value)) { |
564
|
2 |
|
if ($key === 'workSheet') { |
565
|
|
|
// Detach from worksheet |
566
|
2 |
|
$this->{$key} = null; |
567
|
|
|
} else { |
568
|
2 |
|
$this->{$key} = clone $value; |
569
|
|
|
} |
570
|
2 |
|
} elseif ((is_array($value)) && ($key === 'columns')) { |
571
|
|
|
// The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table objects |
572
|
2 |
|
$this->{$key} = []; |
573
|
2 |
|
foreach ($value as $k => $v) { |
574
|
|
|
/** @var Table\Column $v */ |
575
|
1 |
|
$this->{$key}[$k] = clone $v; |
576
|
|
|
// attach the new cloned Column to this new cloned Table object |
577
|
1 |
|
$this->{$key}[$k]->setTable($this); |
578
|
|
|
} |
579
|
|
|
} else { |
580
|
2 |
|
$this->{$key} = $value; |
581
|
|
|
} |
582
|
|
|
} |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
/** |
586
|
|
|
* toString method replicates previous behavior by returning the range if object is |
587
|
|
|
* referenced as a property of its worksheet. |
588
|
|
|
*/ |
589
|
1 |
|
public function __toString(): string |
590
|
|
|
{ |
591
|
1 |
|
return (string) $this->range; |
592
|
|
|
} |
593
|
|
|
} |
594
|
|
|
|