|
4 | 4 |
|
5 | 5 | use Carbon\Carbon; |
6 | 6 | use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes; |
| 7 | +use Illuminate\Database\Eloquent\Builder; |
7 | 8 | use Illuminate\Database\Eloquent\Model; |
8 | 9 | use Illuminate\Support\Collection; |
9 | 10 | use Illuminate\Support\Facades\Cache; |
@@ -47,6 +48,13 @@ abstract class BaseModel extends Model |
47 | 48 | protected $hidden = [ |
48 | 49 | 'laravel_through_key' |
49 | 50 | ]; |
| 51 | + |
| 52 | + /** |
| 53 | + * Temporary cache to avoid multiple getDirty calls generating multiple set calls for |
| 54 | + * sync/merge casted attributes to objects to persist the possible changes made to those objects |
| 55 | + */ |
| 56 | + protected ?array $tmpDirtyCache = null; |
| 57 | + |
50 | 58 | private array $incrementsToRefresh = []; |
51 | 59 |
|
52 | 60 | /** |
@@ -591,6 +599,83 @@ public function syncOriginalAttributes($attributes): static |
591 | 599 | return $this; |
592 | 600 | } |
593 | 601 |
|
| 602 | + /** |
| 603 | + * @inheritdoc |
| 604 | + */ |
| 605 | + public function isDirty($attributes = null): bool |
| 606 | + { |
| 607 | + return [] !== $this->getDirty(\is_array($attributes) ? $attributes : \func_get_args()); |
| 608 | + } |
| 609 | + |
| 610 | + /** |
| 611 | + * Get the attributes that have been changed since the last sync. |
| 612 | + * @param string|array $attributes |
| 613 | + * @return array |
| 614 | + */ |
| 615 | + public function getDirty(): array |
| 616 | + { |
| 617 | + if (isset($this->tmpDirtyCache)) { |
| 618 | + if ([] !== $args = \func_get_args()) { |
| 619 | + return \array_intersect_key($this->tmpDirtyCache, \array_flip((array)$args[0])); |
| 620 | + } |
| 621 | + |
| 622 | + return $this->tmpDirtyCache; |
| 623 | + } |
| 624 | + |
| 625 | + $attributes = (array)(\func_get_args()[0] ?? \array_keys($this->attributes)); |
| 626 | + |
| 627 | + $dirty = []; |
| 628 | + |
| 629 | + foreach ($attributes as $key) { |
| 630 | + // this will merge/sync before the if condition |
| 631 | + $value = $this->getAttributeFromArray($key); |
| 632 | + |
| 633 | + if (!$this->originalIsEquivalent($key)) { |
| 634 | + $dirty[$key] = $value; |
| 635 | + } |
| 636 | + } |
| 637 | + |
| 638 | + return $dirty; |
| 639 | + } |
| 640 | + |
| 641 | + /** |
| 642 | + * @inheritdoc |
| 643 | + */ |
| 644 | + protected function performUpdate(Builder $query): bool |
| 645 | + { |
| 646 | + if ($this->fireModelEvent('updating') === false) { |
| 647 | + return false; |
| 648 | + } |
| 649 | + |
| 650 | + if ($this->usesTimestamps()) { |
| 651 | + $this->updateTimestamps(); |
| 652 | + } |
| 653 | + |
| 654 | + // this is needed because updating event might change the model |
| 655 | + $dirty = $this->getDirtyForUpdate(); |
| 656 | + |
| 657 | + if ([] !== $dirty) { |
| 658 | + $this->setKeysForSaveQuery($query)->update($dirty); |
| 659 | + $this->tmpDirtyCache = $dirty; |
| 660 | + $this->syncChanges(); |
| 661 | + unset($this->tmpDirtyCache); |
| 662 | + |
| 663 | + $this->fireModelEvent('updated', false); |
| 664 | + } |
| 665 | + |
| 666 | + return true; |
| 667 | + } |
| 668 | + |
| 669 | + /** |
| 670 | + * Get the attributes that have been changed since the last sync for an update operation. |
| 671 | + * |
| 672 | + * @return array |
| 673 | + */ |
| 674 | + protected function getDirtyForUpdate(): array |
| 675 | + { |
| 676 | + return $this->getDirty(); |
| 677 | + } |
| 678 | + |
594 | 679 | /** |
595 | 680 | * Get an attribute from the $attributes array without transformation |
596 | 681 | * @see self::getAttributeValue |
|
0 commit comments