Passed
Push — master ( 9e1378...d02904 )
by Mark
16:48 queued 06:34
created

Cell::getParent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Cell;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
7
use PhpOffice\PhpSpreadsheet\Collection\Cells;
8
use PhpOffice\PhpSpreadsheet\Exception;
9
use PhpOffice\PhpSpreadsheet\RichText\RichText;
10
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
11
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
12
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellStyleAssessor;
13
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
14
use PhpOffice\PhpSpreadsheet\Style\Style;
15
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
16
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
17
18
class Cell
19
{
20
    /**
21
     * Value binder to use.
22
     *
23
     * @var IValueBinder
24
     */
25
    private static $valueBinder;
26
27
    /**
28
     * Value of the cell.
29
     *
30
     * @var mixed
31
     */
32
    private $value;
33
34
    /**
35
     *    Calculated value of the cell (used for caching)
36
     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
37
     *        create the original spreadsheet file.
38
     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
39
     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
40
     *        values used by the formula have changed since it was last calculated.
41
     *
42
     * @var mixed
43
     */
44
    private $calculatedValue;
45
46
    /**
47
     * Type of the cell data.
48
     *
49
     * @var string
50
     */
51
    private $dataType;
52
53
    /**
54
     * The collection of cells that this cell belongs to (i.e. The Cell Collection for the parent Worksheet).
55
     *
56
     * @var ?Cells
57
     */
58
    private $parent;
59
60
    /**
61
     * Index to the cellXf reference for the styling of this cell.
62
     *
63
     * @var int
64
     */
65
    private $xfIndex = 0;
66
67
    /**
68
     * Attributes of the formula.
69
     *
70
     * @var mixed
71
     */
72
    private $formulaAttributes;
73
74
    /**
75
     * Update the cell into the cell collection.
76
     *
77
     * @return $this
78
     */
79 9762
    public function updateInCollection(): self
80
    {
81 9762
        $parent = $this->parent;
82 9762
        if ($parent === null) {
83 2
            throw new Exception('Cannot update when cell is not bound to a worksheet');
84
        }
85 9760
        $parent->update($this);
86
87 9760
        return $this;
88
    }
89
90 9346
    public function detach(): void
91
    {
92 9346
        $this->parent = null;
93
    }
94
95 8771
    public function attach(Cells $parent): void
96
    {
97 8771
        $this->parent = $parent;
98
    }
99
100
    /**
101
     * Create a new Cell.
102
     *
103
     * @param mixed $value
104
     */
105 9811
    public function __construct($value, ?string $dataType, Worksheet $worksheet)
106
    {
107
        // Initialise cell value
108 9811
        $this->value = $value;
109
110
        // Set worksheet cache
111 9811
        $this->parent = $worksheet->getCellCollection();
112
113
        // Set datatype?
114 9811
        if ($dataType !== null) {
115 9811
            if ($dataType == DataType::TYPE_STRING2) {
116
                $dataType = DataType::TYPE_STRING;
117
            }
118 9811
            $this->dataType = $dataType;
119
        } elseif (self::getValueBinder()->bindValue($this, $value) === false) {
120
            throw new Exception('Value could not be bound to cell.');
121
        }
122
    }
123
124
    /**
125
     * Get cell coordinate column.
126
     *
127
     * @return string
128
     */
129 499
    public function getColumn()
130
    {
131 499
        $parent = $this->parent;
132 499
        if ($parent === null) {
133 1
            throw new Exception('Cannot get column when cell is not bound to a worksheet');
134
        }
135
136 498
        return $parent->getCurrentColumn();
137
    }
138
139
    /**
140
     * Get cell coordinate row.
141
     *
142
     * @return int
143
     */
144 483
    public function getRow()
145
    {
146 483
        $parent = $this->parent;
147 483
        if ($parent === null) {
148 1
            throw new Exception('Cannot get row when cell is not bound to a worksheet');
149
        }
150
151 482
        return $parent->getCurrentRow();
152
    }
153
154
    /**
155
     * Get cell coordinate.
156
     *
157
     * @return string
158
     */
159 9767
    public function getCoordinate()
160
    {
161 9767
        $parent = $this->parent;
162 9767
        if ($parent !== null) {
163 9767
            $coordinate = $parent->getCurrentCoordinate();
164
        } else {
165 2
            $coordinate = null;
166
        }
167 9767
        if ($coordinate === null) {
168 2
            throw new Exception('Coordinate no longer exists');
169
        }
170
171 9767
        return $coordinate;
172
    }
173
174
    /**
175
     * Get cell value.
176
     *
177
     * @return mixed
178
     */
179 9210
    public function getValue()
180
    {
181 9210
        return $this->value;
182
    }
183
184
    /**
185
     * Get cell value with formatting.
186
     */
187 83
    public function getFormattedValue(): string
188
    {
189 83
        return (string) NumberFormat::toFormattedString(
190 83
            $this->getCalculatedValue(),
191 83
            (string) $this->getStyle()->getNumberFormat()->getFormatCode()
192 83
        );
193
    }
194
195
    /**
196
     * @param mixed $oldValue
197
     * @param mixed $newValue
198
     */
199 9734
    protected static function updateIfCellIsTableHeader(?Worksheet $workSheet, self $cell, $oldValue, $newValue): void
200
    {
201 9734
        if (StringHelper::strToLower($oldValue ?? '') === StringHelper::strToLower($newValue ?? '') || $workSheet === null) {
202 727
            return;
203
        }
204
205 9700
        foreach ($workSheet->getTableCollection() as $table) {
206
            /** @var Table $table */
207 8
            if ($cell->isInRange($table->getRange())) {
208 6
                $rangeRowsColumns = Coordinate::getRangeBoundaries($table->getRange());
209 6
                if ($cell->getRow() === (int) $rangeRowsColumns[0][1]) {
210 3
                    Table\Column::updateStructuredReferences($workSheet, $oldValue, $newValue);
211
                }
212
213 6
                return;
214
            }
215
        }
216
    }
217
218
    /**
219
     * Set cell value.
220
     *
221
     *    Sets the value for a cell, automatically determining the datatype using the value binder
222
     *
223
     * @param mixed $value Value
224
     * @param null|IValueBinder $binder Value Binder to override the currently set Value Binder
225
     *
226
     * @throws Exception
227
     *
228
     * @return $this
229
     */
230 9500
    public function setValue($value, ?IValueBinder $binder = null): self
231
    {
232 9500
        $binder ??= self::getValueBinder();
233 9500
        if (!$binder->bindValue($this, $value)) {
234
            throw new Exception('Value could not be bound to cell.');
235
        }
236
237 9499
        return $this;
238
    }
239
240
    /**
241
     * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder).
242
     *
243
     * @param mixed $value Value
244
     * @param string $dataType Explicit data type, see DataType::TYPE_*
245
     *        Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
246
     *             method, then it is your responsibility as an end-user developer to validate that the value and
247
     *             the datatype match.
248
     *       If you do mismatch value and datatype, then the value you enter may be changed to match the datatype
249
     *          that you specify.
250
     *
251
     * @return Cell
252
     */
253 9737
    public function setValueExplicit($value, string $dataType = DataType::TYPE_STRING)
254
    {
255 9737
        $oldValue = $this->value;
256
257
        // set the value according to data type
258
        switch ($dataType) {
259 255
            case DataType::TYPE_NULL:
260 474
                $this->value = null;
261
262 474
                break;
263 254
            case DataType::TYPE_STRING2:
264 2
                $dataType = DataType::TYPE_STRING;
265
                // no break
266 254
            case DataType::TYPE_STRING:
267
                // Synonym for string
268 230
            case DataType::TYPE_INLINE:
269
                // Rich text
270 5248
                $this->value = DataType::checkString($value);
271
272 5248
                break;
273 229
            case DataType::TYPE_NUMERIC:
274 6602
                if (is_string($value) && !is_numeric($value)) {
275 1
                    throw new Exception('Invalid numeric value for datatype Numeric');
276
                }
277 6601
                $this->value = 0 + $value;
278
279 6601
                break;
280 190
            case DataType::TYPE_FORMULA:
281 8446
                $this->value = (string) $value;
282
283 8446
                break;
284 17
            case DataType::TYPE_BOOL:
285 476
                $this->value = (bool) $value;
286
287 476
                break;
288
            case DataType::TYPE_ISO_DATE:
289 5
                $this->value = SharedDate::convertIsoDate($value);
290 4
                $dataType = DataType::TYPE_NUMERIC;
291
292 4
                break;
293
            case DataType::TYPE_ERROR:
294 19
                $this->value = DataType::checkErrorCode($value);
295
296 19
                break;
297
            default:
298
                throw new Exception('Invalid datatype: ' . $dataType);
299
        }
300
301
        // set the datatype
302 9735
        $this->dataType = $dataType;
303
304 9735
        $this->updateInCollection();
305 9734
        $cellCoordinate = $this->getCoordinate();
306 9734
        self::updateIfCellIsTableHeader($this->getParent()->getParent(), $this, $oldValue, $value); // @phpstan-ignore-line
307
308 9734
        return $this->getParent()->get($cellCoordinate); // @phpstan-ignore-line
309
    }
310
311
    public const CALCULATE_DATE_TIME_ASIS = 0;
312
    public const CALCULATE_DATE_TIME_FLOAT = 1;
313
    public const CALCULATE_TIME_FLOAT = 2;
314
315
    /** @var int */
316
    private static $calculateDateTimeType = self::CALCULATE_DATE_TIME_ASIS;
317
318 45
    public static function getCalculateDateTimeType(): int
319
    {
320 45
        return self::$calculateDateTimeType;
321
    }
322
323 45
    public static function setCalculateDateTimeType(int $calculateDateTimeType): void
324
    {
325
        switch ($calculateDateTimeType) {
326 45
            case self::CALCULATE_DATE_TIME_ASIS:
327 23
            case self::CALCULATE_DATE_TIME_FLOAT:
328 12
            case self::CALCULATE_TIME_FLOAT:
329 45
                self::$calculateDateTimeType = $calculateDateTimeType;
330
331 45
                break;
332
            default:
333 1
                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception("Invalid value $calculateDateTimeType for calculated date time type");
334
        }
335
    }
336
337
    /**
338
     * Convert date, time, or datetime from int to float if desired.
339
     *
340
     * @param mixed $result
341
     *
342
     * @return mixed
343
     */
344 8760
    private function convertDateTimeInt($result)
345
    {
346 8760
        if (is_int($result)) {
347 4212
            if (self::$calculateDateTimeType === self::CALCULATE_TIME_FLOAT) {
348 4
                if (SharedDate::isDateTime($this, $result, false)) {
349 4
                    $result = (float) $result;
350
                }
351 4208
            } elseif (self::$calculateDateTimeType === self::CALCULATE_DATE_TIME_FLOAT) {
352 4
                if (SharedDate::isDateTime($this, $result, true)) {
353 3
                    $result = (float) $result;
354
                }
355
            }
356
        }
357
358 8760
        return $result;
359
    }
360
361
    /**
362
     * Get calculated cell value.
363
     *
364
     * @param bool $resetLog Whether the calculation engine logger should be reset or not
365
     *
366
     * @return mixed
367
     */
368 8961
    public function getCalculatedValue(bool $resetLog = true)
369
    {
370 8961
        if ($this->dataType === DataType::TYPE_FORMULA) {
371
            try {
372 8158
                $index = $this->getWorksheet()->getParentOrThrow()->getActiveSheetIndex();
373 8158
                $selected = $this->getWorksheet()->getSelectedCells();
374 8158
                $result = Calculation::getInstance(
375 8158
                    $this->getWorksheet()->getParent()
376 8158
                )->calculateCellValue($this, $resetLog);
377 7955
                $result = $this->convertDateTimeInt($result);
378 7955
                $this->getWorksheet()->setSelectedCells($selected);
379 7955
                $this->getWorksheet()->getParentOrThrow()->setActiveSheetIndex($index);
380
                //    We don't yet handle array returns
381 7955
                if (is_array($result)) {
382 7955
                    while (is_array($result)) {
383 3729
                        $result = array_shift($result);
384
                    }
385
                }
386 221
            } catch (Exception $ex) {
387 221
                if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
388 1
                    return $this->calculatedValue; // Fallback for calculations referencing external files.
389 221
                } elseif (preg_match('/[Uu]ndefined (name|offset: 2|array key 2)/', $ex->getMessage()) === 1) {
390 29
                    return ExcelError::NAME();
391
                }
392
393 192
                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(
394 192
                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage()
395 192
                );
396
            }
397
398 7955
            if ($result === '#Not Yet Implemented') {
399 3
                return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
400
            }
401
402 7955
            return $result;
403 6264
        } elseif ($this->value instanceof RichText) {
404 19
            return $this->value->getPlainText();
405
        }
406
407 6259
        return $this->convertDateTimeInt($this->value);
408
    }
409
410
    /**
411
     * Set old calculated value (cached).
412
     *
413
     * @param mixed $originalValue Value
414
     */
415 359
    public function setCalculatedValue($originalValue): self
416
    {
417 359
        if ($originalValue !== null) {
418 359
            $this->calculatedValue = (is_numeric($originalValue)) ? (float) $originalValue : $originalValue;
419
        }
420
421 359
        return $this->updateInCollection();
422
    }
423
424
    /**
425
     *    Get old calculated value (cached)
426
     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
427
     *        create the original spreadsheet file.
428
     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
429
     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
430
     *        values used by the formula have changed since it was last calculated.
431
     *
432
     * @return mixed
433
     */
434 3
    public function getOldCalculatedValue()
435
    {
436 3
        return $this->calculatedValue;
437
    }
438
439
    /**
440
     * Get cell data type.
441
     */
442 926
    public function getDataType(): string
443
    {
444 926
        return $this->dataType;
445
    }
446
447
    /**
448
     * Set cell data type.
449
     *
450
     * @param string $dataType see DataType::TYPE_*
451
     */
452
    public function setDataType($dataType): self
453
    {
454
        if ($dataType == DataType::TYPE_STRING2) {
455
            $dataType = DataType::TYPE_STRING;
456
        }
457
        $this->dataType = $dataType;
458
459
        return $this->updateInCollection();
460
    }
461
462
    /**
463
     * Identify if the cell contains a formula.
464
     */
465 65
    public function isFormula(): bool
466
    {
467 65
        return $this->dataType === DataType::TYPE_FORMULA && $this->getStyle()->getQuotePrefix() === false;
468
    }
469
470
    /**
471
     *    Does this cell contain Data validation rules?
472
     */
473 13
    public function hasDataValidation(): bool
474
    {
475 13
        if (!isset($this->parent)) {
476 1
            throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
477
        }
478
479 12
        return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
480
    }
481
482
    /**
483
     * Get Data validation rules.
484
     */
485 23
    public function getDataValidation(): DataValidation
486
    {
487 23
        if (!isset($this->parent)) {
488 1
            throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
489
        }
490
491 22
        return $this->getWorksheet()->getDataValidation($this->getCoordinate());
492
    }
493
494
    /**
495
     * Set Data validation rules.
496
     */
497 1
    public function setDataValidation(?DataValidation $dataValidation = null): self
498
    {
499 1
        if (!isset($this->parent)) {
500 1
            throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
501
        }
502
503
        $this->getWorksheet()->setDataValidation($this->getCoordinate(), $dataValidation);
504
505
        return $this->updateInCollection();
506
    }
507
508
    /**
509
     * Does this cell contain valid value?
510
     */
511 4
    public function hasValidValue(): bool
512
    {
513 4
        $validator = new DataValidator();
514
515 4
        return $validator->isValid($this);
516
    }
517
518
    /**
519
     * Does this cell contain a Hyperlink?
520
     */
521 1
    public function hasHyperlink(): bool
522
    {
523 1
        if (!isset($this->parent)) {
524 1
            throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
525
        }
526
527
        return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
528
    }
529
530
    /**
531
     * Get Hyperlink.
532
     */
533 59
    public function getHyperlink(): Hyperlink
534
    {
535 59
        if (!isset($this->parent)) {
536 1
            throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
537
        }
538
539 58
        return $this->getWorksheet()->getHyperlink($this->getCoordinate());
540
    }
541
542
    /**
543
     * Set Hyperlink.
544
     */
545 1
    public function setHyperlink(?Hyperlink $hyperlink = null): self
546
    {
547 1
        if (!isset($this->parent)) {
548 1
            throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
549
        }
550
551
        $this->getWorksheet()->setHyperlink($this->getCoordinate(), $hyperlink);
552
553
        return $this->updateInCollection();
554
    }
555
556
    /**
557
     * Get cell collection.
558
     *
559
     * @return ?Cells
560
     */
561 9736
    public function getParent()
562
    {
563 9736
        return $this->parent;
564
    }
565
566
    /**
567
     * Get parent worksheet.
568
     */
569 8999
    public function getWorksheet(): Worksheet
570
    {
571 8999
        $parent = $this->parent;
572 8999
        if ($parent !== null) {
573 8999
            $worksheet = $parent->getParent();
574
        } else {
575 1
            $worksheet = null;
576
        }
577
578 8999
        if ($worksheet === null) {
579 1
            throw new Exception('Worksheet no longer exists');
580
        }
581
582 8999
        return $worksheet;
583
    }
584
585 9
    public function getWorksheetOrNull(): ?Worksheet
586
    {
587 9
        $parent = $this->parent;
588 9
        if ($parent !== null) {
589 9
            $worksheet = $parent->getParent();
590
        } else {
591
            $worksheet = null;
592
        }
593
594 9
        return $worksheet;
595
    }
596
597
    /**
598
     * Is this cell in a merge range.
599
     */
600 7
    public function isInMergeRange(): bool
601
    {
602 7
        return (bool) $this->getMergeRange();
603
    }
604
605
    /**
606
     * Is this cell the master (top left cell) in a merge range (that holds the actual data value).
607
     */
608 26
    public function isMergeRangeValueCell(): bool
609
    {
610 26
        if ($mergeRange = $this->getMergeRange()) {
611 5
            $mergeRange = Coordinate::splitRange($mergeRange);
612 5
            [$startCell] = $mergeRange[0];
613
614 5
            return $this->getCoordinate() === $startCell;
615
        }
616
617 23
        return false;
618
    }
619
620
    /**
621
     * If this cell is in a merge range, then return the range.
622
     *
623
     * @return false|string
624
     */
625 29
    public function getMergeRange()
626
    {
627 29
        foreach ($this->getWorksheet()->getMergeCells() as $mergeRange) {
628 5
            if ($this->isInRange($mergeRange)) {
629 5
                return $mergeRange;
630
            }
631
        }
632
633 26
        return false;
634
    }
635
636
    /**
637
     * Get cell style.
638
     */
639 8886
    public function getStyle(): Style
640
    {
641 8886
        return $this->getWorksheet()->getStyle($this->getCoordinate());
642
    }
643
644
    /**
645
     * Get cell style.
646
     */
647 6
    public function getAppliedStyle(): Style
648
    {
649 6
        if ($this->getWorksheet()->conditionalStylesExists($this->getCoordinate()) === false) {
650 2
            return $this->getStyle();
651
        }
652 4
        $range = $this->getWorksheet()->getConditionalRange($this->getCoordinate());
653 4
        if ($range === null) {
654
            return $this->getStyle();
655
        }
656
657 4
        $matcher = new CellStyleAssessor($this, $range);
658
659 4
        return $matcher->matchConditions($this->getWorksheet()->getConditionalStyles($this->getCoordinate()));
660
    }
661
662
    /**
663
     * Re-bind parent.
664
     */
665
    public function rebindParent(Worksheet $parent): self
666
    {
667
        $this->parent = $parent->getCellCollection();
668
669
        return $this->updateInCollection();
670
    }
671
672
    /**
673
     *    Is cell in a specific range?
674
     *
675
     * @param string $range Cell range (e.g. A1:A1)
676
     */
677 229
    public function isInRange(string $range): bool
678
    {
679 229
        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($range);
680
681
        // Translate properties
682 229
        $myColumn = Coordinate::columnIndexFromString($this->getColumn());
683 229
        $myRow = $this->getRow();
684
685
        // Verify if cell is in range
686 229
        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
687 229
                ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
688
    }
689
690
    /**
691
     * Compare 2 cells.
692
     *
693
     * @param Cell $a Cell a
694
     * @param Cell $b Cell b
695
     *
696
     * @return int Result of comparison (always -1 or 1, never zero!)
697
     */
698
    public static function compareCells(self $a, self $b): int
699
    {
700
        if ($a->getRow() < $b->getRow()) {
701
            return -1;
702
        } elseif ($a->getRow() > $b->getRow()) {
703
            return 1;
704
        } elseif (Coordinate::columnIndexFromString($a->getColumn()) < Coordinate::columnIndexFromString($b->getColumn())) {
705
            return -1;
706
        }
707
708
        return 1;
709
    }
710
711
    /**
712
     * Get value binder to use.
713
     */
714 9501
    public static function getValueBinder(): IValueBinder
715
    {
716 9501
        if (self::$valueBinder === null) {
717 231
            self::$valueBinder = new DefaultValueBinder();
718
        }
719
720 9501
        return self::$valueBinder;
721
    }
722
723
    /**
724
     * Set value binder to use.
725
     */
726 133
    public static function setValueBinder(IValueBinder $binder): void
727
    {
728 133
        self::$valueBinder = $binder;
729
    }
730
731
    /**
732
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
733
     */
734 6
    public function __clone()
735
    {
736 6
        $vars = get_object_vars($this);
737 6
        foreach ($vars as $propertyName => $propertyValue) {
738 6
            if ((is_object($propertyValue)) && ($propertyName !== 'parent')) {
739
                $this->$propertyName = clone $propertyValue;
740
            } else {
741 6
                $this->$propertyName = $propertyValue;
742
            }
743
        }
744
    }
745
746
    /**
747
     * Get index to cellXf.
748
     */
749 9267
    public function getXfIndex(): int
750
    {
751 9267
        return $this->xfIndex;
752
    }
753
754
    /**
755
     * Set index to cellXf.
756
     */
757 1396
    public function setXfIndex(int $indexValue): self
758
    {
759 1396
        $this->xfIndex = $indexValue;
760
761 1396
        return $this->updateInCollection();
762
    }
763
764
    /**
765
     * Set the formula attributes.
766
     *
767
     * @param mixed $attributes
768
     *
769
     * @return $this
770
     */
771 2
    public function setFormulaAttributes($attributes): self
772
    {
773 2
        $this->formulaAttributes = $attributes;
774
775 2
        return $this;
776
    }
777
778
    /**
779
     * Get the formula attributes.
780
     *
781
     * @return mixed
782
     */
783 72
    public function getFormulaAttributes()
784
    {
785 72
        return $this->formulaAttributes;
786
    }
787
788
    /**
789
     * Convert to string.
790
     *
791
     * @return string
792
     */
793
    public function __toString()
794
    {
795
        return (string) $this->getValue();
796
    }
797
}
798