From 25626acce5b99ec93473e83c41ccf1eb99c8a3bd Mon Sep 17 00:00:00 2001 From: Marko Ilic Date: Tue, 20 Jul 2021 13:14:44 +0200 Subject: [PATCH 1/3] feat: add reverse owning to embed one field denormalizer --- .../Relation/EmbedOneFieldDenormalizer.php | 20 +++- src/DeserializerLogicException.php | 5 + .../EmbedOneFieldDenormalizerTest.php | 91 +++++++++++++++++++ tests/Unit/DeserializerLogicExceptionTest.php | 7 ++ 4 files changed, 120 insertions(+), 3 deletions(-) diff --git a/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php b/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php index 37883b5..62cb357 100644 --- a/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php +++ b/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php @@ -17,10 +17,13 @@ final class EmbedOneFieldDenormalizer implements FieldDenormalizerInterface private AccessorInterface $accessor; - public function __construct(string $class, AccessorInterface $accessor) + private ?AccessorInterface $parentAccessor; + + public function __construct(string $class, AccessorInterface $accessor, ?AccessorInterface $parentAccessor = null) { $this->class = $class; $this->accessor = $accessor; + $this->parentAccessor = $parentAccessor; } /** @@ -34,7 +37,8 @@ public function denormalizeField( object $object, $value, DenormalizerContextInterface $context, - ?DenormalizerInterface $denormalizer = null + ?DenormalizerInterface $denormalizer = null, + bool $hasReverseOwning = false ): void { if (null === $value) { $this->accessor->setValue($object, $value); @@ -52,6 +56,16 @@ public function denormalizeField( $relatedObject = $this->accessor->getValue($object) ?? $this->class; - $this->accessor->setValue($object, $denormalizer->denormalize($relatedObject, $value, $context, $path)); + $denormalizedRelatedObject = $denormalizer->denormalize($relatedObject, $value, $context, $path); + + $this->accessor->setValue($object, $denormalizedRelatedObject); + + if (true === $hasReverseOwning) { + if (null === $this->parentAccessor) { + throw DeserializerLogicException::createMissingParentAccessor($path); + } + + $this->parentAccessor->setValue($denormalizedRelatedObject, $object); + } } } diff --git a/src/DeserializerLogicException.php b/src/DeserializerLogicException.php index 9f384f8..b19e2bb 100644 --- a/src/DeserializerLogicException.php +++ b/src/DeserializerLogicException.php @@ -45,4 +45,9 @@ public static function createConvertTypeDoesNotExists(string $convertType): self { return new self(sprintf('Convert type "%s" is not supported', $convertType)); } + + public static function createMissingParentAccessor(string $path): self + { + return new self(sprintf('There is no parent accessor at path: "%s"', $path)); + } } diff --git a/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php b/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php index a2ca823..1be84ee 100644 --- a/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php +++ b/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php @@ -131,4 +131,95 @@ public function testDenormalizeFieldWithExistingValue(): void $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor); $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer); } + + public function testDenormalizeFieldWithMissingParentAccessor(): void + { + $this->expectException(DeserializerLogicException::class); + $this->expectExceptionMessage('There is no parent accessor at path: "reference"'); + + $object = new \stdClass(); + + $reference = new \stdClass(); + + /** @var AccessorInterface|MockObject $accessor */ + $accessor = $this->getMockByCalls(AccessorInterface::class, [ + Call::create('getValue')->with($object)->willReturn(null), + Call::create('setValue')->with($object, $reference), + ]); + + /** @var DenormalizerContextInterface|MockObject $context */ + $context = $this->getMockByCalls(DenormalizerContextInterface::class); + + /** @var DenormalizerInterface|MockObject $denormalizer */ + $denormalizer = $this->getMockByCalls(DenormalizerInterface::class, [ + Call::create('denormalize') + ->with(\stdClass::class, ['name' => 'name'], $context, 'reference') + ->willReturn($reference), + ]); + + $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor); + $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer, true); + } + + public function testDenormalizeFieldWithReverseOwning(): void + { + $object = new \stdClass(); + + $reference = new \stdClass(); + + /** @var AccessorInterface|MockObject $accessor */ + $accessor = $this->getMockByCalls(AccessorInterface::class, [ + Call::create('getValue')->with($object)->willReturn(null), + Call::create('setValue')->with($object, $reference), + ]); + + /** @var DenormalizerContextInterface|MockObject $context */ + $context = $this->getMockByCalls(DenormalizerContextInterface::class); + + /** @var DenormalizerInterface|MockObject $denormalizer */ + $denormalizer = $this->getMockByCalls(DenormalizerInterface::class, [ + Call::create('denormalize') + ->with(\stdClass::class, ['name' => 'name'], $context, 'reference') + ->willReturn($reference), + ]); + + /** @var AccessorInterface|MockObject $parentAccessor */ + $parentAccessor = $this->getMockByCalls(AccessorInterface::class, [ + Call::create('setValue')->with($reference, $object), + ]); + + $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor, $parentAccessor); + $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer, true); + } + + public function testDenormalizeFieldWithExistingValueAndWithReverseOwning(): void + { + $object = new \stdClass(); + + $reference = new \stdClass(); + + /** @var AccessorInterface|MockObject $accessor */ + $accessor = $this->getMockByCalls(AccessorInterface::class, [ + Call::create('getValue')->with($object)->willReturn($reference), + Call::create('setValue')->with($object, $reference), + ]); + + /** @var DenormalizerContextInterface|MockObject $context */ + $context = $this->getMockByCalls(DenormalizerContextInterface::class); + + /** @var DenormalizerInterface|MockObject $denormalizer */ + $denormalizer = $this->getMockByCalls(DenormalizerInterface::class, [ + Call::create('denormalize') + ->with($reference, ['name' => 'name'], $context, 'reference') + ->willReturn($reference), + ]); + + /** @var AccessorInterface|MockObject $parentAccessor */ + $parentAccessor = $this->getMockByCalls(AccessorInterface::class, [ + Call::create('setValue')->with($reference, $object), + ]); + + $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor, $parentAccessor); + $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer, true); + } } diff --git a/tests/Unit/DeserializerLogicExceptionTest.php b/tests/Unit/DeserializerLogicExceptionTest.php index 85ba13f..646ea00 100644 --- a/tests/Unit/DeserializerLogicExceptionTest.php +++ b/tests/Unit/DeserializerLogicExceptionTest.php @@ -65,4 +65,11 @@ public function testCreateConvertTypeDoesNotExists(): void self::assertSame('Convert type "type" is not supported', $exception->getMessage()); } + + public function testCreateMissingParentAccessor(): void + { + $exception = DeserializerLogicException::createMissingParentAccessor('path1'); + + self::assertSame('There is no parent accessor at path: "path1"', $exception->getMessage()); + } } From 0eacc4e30119af48e4f9b4e0ae5ca3de49119fe0 Mon Sep 17 00:00:00 2001 From: Marko Ilic Date: Tue, 20 Jul 2021 13:28:15 +0200 Subject: [PATCH 2/3] remove hasReverseOwning paramter --- .../Relation/EmbedOneFieldDenormalizer.php | 9 ++--- src/DeserializerLogicException.php | 5 --- .../EmbedOneFieldDenormalizerTest.php | 33 ++----------------- tests/Unit/DeserializerLogicExceptionTest.php | 7 ---- 4 files changed, 4 insertions(+), 50 deletions(-) diff --git a/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php b/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php index 62cb357..669047b 100644 --- a/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php +++ b/src/Denormalizer/Relation/EmbedOneFieldDenormalizer.php @@ -37,8 +37,7 @@ public function denormalizeField( object $object, $value, DenormalizerContextInterface $context, - ?DenormalizerInterface $denormalizer = null, - bool $hasReverseOwning = false + ?DenormalizerInterface $denormalizer = null ): void { if (null === $value) { $this->accessor->setValue($object, $value); @@ -60,11 +59,7 @@ public function denormalizeField( $this->accessor->setValue($object, $denormalizedRelatedObject); - if (true === $hasReverseOwning) { - if (null === $this->parentAccessor) { - throw DeserializerLogicException::createMissingParentAccessor($path); - } - + if (null !== $this->parentAccessor) { $this->parentAccessor->setValue($denormalizedRelatedObject, $object); } } diff --git a/src/DeserializerLogicException.php b/src/DeserializerLogicException.php index b19e2bb..9f384f8 100644 --- a/src/DeserializerLogicException.php +++ b/src/DeserializerLogicException.php @@ -45,9 +45,4 @@ public static function createConvertTypeDoesNotExists(string $convertType): self { return new self(sprintf('Convert type "%s" is not supported', $convertType)); } - - public static function createMissingParentAccessor(string $path): self - { - return new self(sprintf('There is no parent accessor at path: "%s"', $path)); - } } diff --git a/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php b/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php index 1be84ee..05de5a6 100644 --- a/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php +++ b/tests/Unit/Denormalizer/Relation/EmbedOneFieldDenormalizerTest.php @@ -132,35 +132,6 @@ public function testDenormalizeFieldWithExistingValue(): void $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer); } - public function testDenormalizeFieldWithMissingParentAccessor(): void - { - $this->expectException(DeserializerLogicException::class); - $this->expectExceptionMessage('There is no parent accessor at path: "reference"'); - - $object = new \stdClass(); - - $reference = new \stdClass(); - - /** @var AccessorInterface|MockObject $accessor */ - $accessor = $this->getMockByCalls(AccessorInterface::class, [ - Call::create('getValue')->with($object)->willReturn(null), - Call::create('setValue')->with($object, $reference), - ]); - - /** @var DenormalizerContextInterface|MockObject $context */ - $context = $this->getMockByCalls(DenormalizerContextInterface::class); - - /** @var DenormalizerInterface|MockObject $denormalizer */ - $denormalizer = $this->getMockByCalls(DenormalizerInterface::class, [ - Call::create('denormalize') - ->with(\stdClass::class, ['name' => 'name'], $context, 'reference') - ->willReturn($reference), - ]); - - $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor); - $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer, true); - } - public function testDenormalizeFieldWithReverseOwning(): void { $object = new \stdClass(); @@ -189,7 +160,7 @@ public function testDenormalizeFieldWithReverseOwning(): void ]); $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor, $parentAccessor); - $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer, true); + $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer); } public function testDenormalizeFieldWithExistingValueAndWithReverseOwning(): void @@ -220,6 +191,6 @@ public function testDenormalizeFieldWithExistingValueAndWithReverseOwning(): voi ]); $fieldDenormalizer = new EmbedOneFieldDenormalizer(\stdClass::class, $accessor, $parentAccessor); - $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer, true); + $fieldDenormalizer->denormalizeField('reference', $object, ['name' => 'name'], $context, $denormalizer); } } diff --git a/tests/Unit/DeserializerLogicExceptionTest.php b/tests/Unit/DeserializerLogicExceptionTest.php index 646ea00..85ba13f 100644 --- a/tests/Unit/DeserializerLogicExceptionTest.php +++ b/tests/Unit/DeserializerLogicExceptionTest.php @@ -65,11 +65,4 @@ public function testCreateConvertTypeDoesNotExists(): void self::assertSame('Convert type "type" is not supported', $exception->getMessage()); } - - public function testCreateMissingParentAccessor(): void - { - $exception = DeserializerLogicException::createMissingParentAccessor('path1'); - - self::assertSame('There is no parent accessor at path: "path1"', $exception->getMessage()); - } } From ffc8d90a92f108d81b9e4d077fb363669fe24ba4 Mon Sep 17 00:00:00 2001 From: Marko Ilic Date: Tue, 20 Jul 2021 14:59:35 +0200 Subject: [PATCH 3/3] update version alias in composer.json and readme --- README.md | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4564b72..abb6a8e 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ A simple deserialization. Through [Composer](http://getcomposer.org) as [chubbyphp/chubbyphp-deserialization][1]. ```sh -composer require chubbyphp/chubbyphp-deserialization "^3.1" +composer require chubbyphp/chubbyphp-deserialization "^3.2" ``` ## Usage diff --git a/composer.json b/composer.json index 9bcea82..5a03106 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } }, "scripts": {