From 6a2b8b357c6b1512ffc5a0ca3d26b4a7f4634a61 Mon Sep 17 00:00:00 2001 From: Fabio Capucci Date: Tue, 6 Dec 2022 12:52:42 +0100 Subject: [PATCH] improved eloquent integration with dirty tracking --- config/temporal.php | 8 ++--- .../Eloquent/TemporalEloquentSerialize.php | 36 +++++++++++++++---- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/config/temporal.php b/config/temporal.php index ac7c147..3a9f0cf 100644 --- a/config/temporal.php +++ b/config/temporal.php @@ -122,11 +122,11 @@ 'deserialize_attribute_case' => null, /** - * If true adds a `__exists` attribute to the serialized model - * which indicate that the model is saved to database and it is used on deserialization when creating the model. - * If false (or `__exists` is not present) the model will be created as existing model if primary key is present. + * If true adds additional metadata fields (`__exists`, `__dirty`) to the serialized model to improve deserialization. + * `__exists`: indicate that the model is saved to database. + * `__dirty`: indicate that the model has unsaved changes. (original values are not included in the serialized payload but the deserialized model will be marked as dirty) */ - 'include_exists_field' => false, + 'include_metadata_field' => false, ], ], ]; diff --git a/src/Integrations/Eloquent/TemporalEloquentSerialize.php b/src/Integrations/Eloquent/TemporalEloquentSerialize.php index d42e1cd..e567206 100644 --- a/src/Integrations/Eloquent/TemporalEloquentSerialize.php +++ b/src/Integrations/Eloquent/TemporalEloquentSerialize.php @@ -53,7 +53,10 @@ public function toTemporalPayload(): array return Collection::make($this->attributesToArray()) ->merge($relations) ->mapWithKeys(fn (mixed $value, string $key) => [$this->mapAttributeKeyToTemporal($key) => $value]) - ->put('__exists', $this->exists) + ->when(config('temporal.integrations.eloquent.include_metadata_field', false), fn (Collection $collection) => $collection + ->put('__exists', $this->exists) + ->put('__dirty', $this->isDirty()) + ) ->all(); } @@ -72,10 +75,19 @@ public static function fromTemporalPayload(array $payload): static default => [], }); - $instance = $model->newInstance( - $attributes->except($relationships->keys()->merge(['__exists']))->all(), - $attributes->get('__exists', $attributes->get($model->getKeyName()) !== null), - ); + /** @var bool $exists */ + $exists = $attributes->get('__exists', $attributes->get($model->getKeyName()) !== null); + + /** @var bool $dirty */ + $dirty = $attributes->get('__dirty', true); + + $instance = $model->newInstance([], $exists); + + $instance->forceFill($attributes->except($relationships->keys()->merge(['__exists', '__dirty']))->all()); + + if (! $dirty) { + $instance->syncOriginal(); + } foreach ($relationships as $attributeKey => $relationship) { /** @var Relation $relation */ @@ -114,8 +126,20 @@ private static function buildRelatedInstance(Model $relatedModel, ?array $attrib return $relatedModel::fromTemporalPayload($attributes); } + /** @var bool $exists */ $exists = Arr::get($attributes, '__exists', Arr::get($attributes, $relatedModel->getKeyName()) !== null); - return $relatedModel->newInstance(Arr::except($attributes, ['__exists']), $exists); + /** @var bool $dirty */ + $dirty = Arr::get($attributes, '__dirty', true); + + $relatedModelInstance = $relatedModel->newInstance([], $exists); + + $relatedModelInstance->forceFill(Arr::except($attributes, ['__exists', '__dirty'])); + + if ($dirty) { + $relatedModelInstance->syncOriginal(); + } + + return $relatedModelInstance; } }