From 11261ea524b052b792b6c4e387f285cb130eb4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Robles?= Date: Wed, 11 Oct 2023 17:44:23 +0200 Subject: [PATCH] add serialisation and deserialisation to DataTransferObject --- src/DataTransferObject.php | 65 +++++++++++++++++++ .../ValidatedDataTransferObjectTest.php | 29 +++++++++ 2 files changed, 94 insertions(+) diff --git a/src/DataTransferObject.php b/src/DataTransferObject.php index 61e639f..c353787 100644 --- a/src/DataTransferObject.php +++ b/src/DataTransferObject.php @@ -2,12 +2,17 @@ namespace OpenSoutheners\LaravelDto; +use Carbon\Carbon; use Exception; use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Http\Request; use Illuminate\Routing\Route; +use Illuminate\Support\Collection; use Illuminate\Support\Str; +use OpenSoutheners\LaravelDto\Attributes\BindModelUsing; +use OpenSoutheners\LaravelDto\Attributes\BindModelWith; use Symfony\Component\PropertyInfo\Type; abstract class DataTransferObject implements Arrayable @@ -163,4 +168,64 @@ public function fromRequestContext(bool $value = true): self return $this; } + + public function __serialize(): array + { + $reflection = new \ReflectionClass($this); + + /** @var array<\ReflectionProperty> $properties */ + $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC); + + $serialisableArr = []; + + foreach ($properties as $property) { + $key = $property->getName(); + $value = $property->getValue($this); + + /** @var array<\ReflectionAttribute> $propertyAttributes */ + $propertyAttributes = $property->getAttributes(); + $propertyBindingAttributes = [ + 'using' => null, + ]; + + foreach ($propertyAttributes as $attribute) { + $attributeInstance = $attribute->newInstance(); + + if ($attributeInstance instanceof BindModelUsing) { + $propertyBindingAttributes['using'] = $attributeInstance->attribute; + } + } + + $serialisableArr[$key] = match (true) { + $value instanceof Model => $value->getAttribute($propertyBindingAttributes['using'] ?? $value->getRouteKeyName()), + $value instanceof Collection => $value->first() instanceof Model ? $value->map(fn (Model $model) => $model->getAttribute($propertyBindingAttributes['using'] ?? $model->getRouteKeyName()))->join(',') : $value->join(','), + $value instanceof Arrayable => $value->toArray(), + $value instanceof \Stringable => (string) $value, + is_array($value) => head($value) instanceof Model ? implode(',', array_map(fn (Model $model) => $model->getAttribute($propertyBindingAttributes['using'] ?? $model->getRouteKeyName()))) : implode(',', $value), + default => $value, + }; + } + + return $serialisableArr; + } + + /** + * Called during unserialization of the object. + */ + public function __unserialize(array $data): void + { + $properties = (new \ReflectionClass($this))->getProperties(\ReflectionProperty::IS_PUBLIC); + + $propertiesMapper = new PropertiesMapper(array_merge($data), static::class); + + $propertiesMapper->run(); + + $data = $propertiesMapper->get(); + + foreach ($properties as $property) { + $key = $property->getName(); + + $this->{$key} = $data[$key] ?? $property->getDefaultValue(); + } + } } diff --git a/tests/Integration/ValidatedDataTransferObjectTest.php b/tests/Integration/ValidatedDataTransferObjectTest.php index cc53586..d4ad8ee 100644 --- a/tests/Integration/ValidatedDataTransferObjectTest.php +++ b/tests/Integration/ValidatedDataTransferObjectTest.php @@ -97,5 +97,34 @@ public function testDataTransferObjectWithModelSentDoesNotRunQueriesToFetchItAga ]); $this->assertEmpty(DB::getQueryLog()); + $this->assertTrue($data->post->is($post)); + } + + public function testDataTransferObjectCanBeSerializedAndDeserialized() + { + $this->withoutExceptionHandling(); + + Post::factory()->create(); + + Tag::factory()->create(); + Tag::factory()->create(); + + $data = UpdatePostWithRouteBindingData::fromArray([ + 'post' => '1', + 'tags' => '1,2', + 'post_status' => 'test_non_existing_status', + 'published_at' => '2023-09-06 17:35:53', + ]); + + $serializedData = serialize($data); + + $this->assertIsString($serializedData); + + $deserializedData = unserialize($serializedData); + + $this->assertTrue($data->post->is($deserializedData->post)); + $this->assertTrue($deserializedData->post->relationLoaded('tags')); + $this->assertTrue($data->tags->first()->is($deserializedData->tags->first())); + $this->assertTrue($data->publishedAt->eq($deserializedData->publishedAt)); } }