diff --git a/src/Support/EloquentCasts/DataCollectionEloquentCast.php b/src/Support/EloquentCasts/DataCollectionEloquentCast.php index 92e64a11..c253e45c 100644 --- a/src/Support/EloquentCasts/DataCollectionEloquentCast.php +++ b/src/Support/EloquentCasts/DataCollectionEloquentCast.php @@ -122,6 +122,10 @@ public function set($model, string $key, $value, array $attributes): ?string */ public function compare($model, string $key, $firstValue, $secondValue): bool { + if (in_array('encrypted', $this->arguments) && ! empty(Crypt::getPreviousKeys())) { + return false; + } + return $this->get($model, $key, $firstValue, [])?->toArray() === $this->get($model, $key, $secondValue, [])?->toArray(); } diff --git a/src/Support/EloquentCasts/DataEloquentCast.php b/src/Support/EloquentCasts/DataEloquentCast.php index 17d93625..aa65dbc5 100644 --- a/src/Support/EloquentCasts/DataEloquentCast.php +++ b/src/Support/EloquentCasts/DataEloquentCast.php @@ -95,6 +95,10 @@ public function set($model, string $key, $value, array $attributes): ?string */ public function compare($model, string $key, $firstValue, $secondValue): bool { + if (in_array('encrypted', $this->arguments) && ! empty(Crypt::getPreviousKeys())) { + return false; + } + return $this->get($model, $key, $firstValue, [])?->toArray() === $this->get($model, $key, $secondValue, [])?->toArray(); } diff --git a/tests/Support/EloquentCasts/DataCollectionEloquentCastTest.php b/tests/Support/EloquentCasts/DataCollectionEloquentCastTest.php index 05ee4b66..9b82a2ea 100644 --- a/tests/Support/EloquentCasts/DataCollectionEloquentCastTest.php +++ b/tests/Support/EloquentCasts/DataCollectionEloquentCastTest.php @@ -15,6 +15,7 @@ use Spatie\LaravelData\Tests\Fakes\Models\DummyModelWithCasts; use Spatie\LaravelData\Tests\Fakes\Models\DummyModelWithCustomCollectionCasts; use Spatie\LaravelData\Tests\Fakes\Models\DummyModelWithDefaultCasts; +use Spatie\LaravelData\Tests\Fakes\Models\DummyModelWithEncryptedCasts; use Spatie\LaravelData\Tests\Fakes\Models\DummyModelWithJson; use Spatie\LaravelData\Tests\Fakes\MultiData; use Spatie\LaravelData\Tests\Fakes\SimpleData; @@ -265,6 +266,36 @@ public function __construct(public string $b) ->and($model->isDirty('data_collection'))->toBeFalse(); })->skip(fn () => version_compare(app()->version(), '12.18.0', '<')); +it('flags the attribute as dirty when it is encrypted and there are previous encryption keys', function () { + config()->set('app.previous_keys', ['base64:'.base64_encode(random_bytes(32))]); + + $model = new DummyModelWithEncryptedCasts(); + $model->data_collection = [ + new SimpleData('First'), + new SimpleData('Second'), + ]; + $model->save(); + + $model->data_collection = $model->data_collection; + + expect($model->getRawOriginal('data_collection'))->not->toBe($model->getAttributes()['data_collection']) + ->and($model->isDirty('data_collection'))->toBeTrue(); +})->skip(fn () => version_compare(app()->version(), '12.18.0', '<')); + +it('does not flag the attribute as dirty when it is encrypted and there are no previous encryption keys', function () { + $model = new DummyModelWithEncryptedCasts(); + $model->data_collection = [ + new SimpleData('First'), + new SimpleData('Second'), + ]; + $model->save(); + + $model->data_collection = $model->data_collection; + + expect($model->getRawOriginal('data_collection'))->not->toBe($model->getAttributes()['data_collection']) + ->and($model->isDirty('data_collection'))->toBeFalse(); +})->skip(fn () => version_compare(app()->version(), '12.18.0', '<')); + it('can update a model where the cast is initially null', function () { $model = new DummyModelWithCasts(); $model->setRawAttributes(['data_collection' => null]); diff --git a/tests/Support/EloquentCasts/DataEloquentCastTest.php b/tests/Support/EloquentCasts/DataEloquentCastTest.php index 2f5e17c1..8fdc8ad8 100644 --- a/tests/Support/EloquentCasts/DataEloquentCastTest.php +++ b/tests/Support/EloquentCasts/DataEloquentCastTest.php @@ -270,6 +270,30 @@ public function __construct(public string $a) ->and($model->isDirty('data'))->toBeTrue(); })->skip(fn () => version_compare(app()->version(), '12.18.0', '<')); +it('flags the attribute as dirty when it is encrypted and there are previous encryption keys', function () { + config()->set('app.previous_keys', ['base64:'.base64_encode(random_bytes(32))]); + + $model = new DummyModelWithEncryptedCasts(); + $model->data = new SimpleData('First'); + $model->save(); + + $model->data = $model->data; + + expect($model->getRawOriginal('data'))->not->toBe($model->getAttributes()['data']) + ->and($model->isDirty('data'))->toBeTrue(); +})->skip(fn () => version_compare(app()->version(), '12.18.0', '<')); + +it('does not flag the attribute as dirty when it is encrypted and there are no previous encryption keys', function () { + $model = new DummyModelWithEncryptedCasts(); + $model->data = new SimpleData('First'); + $model->save(); + + $model->data = $model->data; + + expect($model->getRawOriginal('data'))->not->toBe($model->getAttributes()['data']) + ->and($model->isDirty('data'))->toBeFalse(); +})->skip(fn () => version_compare(app()->version(), '12.18.0', '<')); + it('can update a model where the cast is initially null', function () { $model = DummyModelWithCasts::create([ 'data' => null,