From 8bf91eefa2f58234bdc0011a37e3026f37ac8038 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Fri, 6 Oct 2023 16:55:48 +0200 Subject: [PATCH 01/10] $sourcePropertyName can be empty to use the complete source object for setting/population --- .../PropertyMappingPopulatorTest.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/Populator/PropertyMappingPopulatorTest.php b/tests/Populator/PropertyMappingPopulatorTest.php index 57af128..c177545 100644 --- a/tests/Populator/PropertyMappingPopulatorTest.php +++ b/tests/Populator/PropertyMappingPopulatorTest.php @@ -6,6 +6,7 @@ use Neusta\ConverterBundle\Populator\PropertyMappingPopulator; use Neusta\ConverterBundle\Tests\Fixtures\Model\Source\Address; +use Neusta\ConverterBundle\Tests\Fixtures\Model\PersonAddress; use Neusta\ConverterBundle\Tests\Fixtures\Model\Source\User; use Neusta\ConverterBundle\Tests\Fixtures\Model\Target\Person; use PHPUnit\Framework\TestCase; @@ -15,7 +16,7 @@ class PropertyMappingPopulatorTest extends TestCase { use ProphecyTrait; - public function test_populate(): void + public function test_populate_a_certain_source_property(): void { $populator = new PropertyMappingPopulator( targetProperty: 'age', @@ -92,4 +93,20 @@ public function test_populate_skip_null_with_sub_fields_and_null_safety(): void self::assertSame('Old City', $target->getPlaceOfResidence()); } + + public function test_populate_whole_source_object(): void + { + $populator = new PropertyMappingPopulator('address', ''); + $address = (new PersonAddress()) + ->setStreet('Street') + ->setStreetNo('1') + ->setCity('Capitol City') + ->setPostalCode('12345'); + + $person = new Person(); + + $populator->populate($person, $address); + + self::assertEquals('Street', $person->getAddress()->getStreet()); + } } From 12b4933d3e99504f3511a6856b9343a2baa4b70a Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Fri, 6 Oct 2023 16:59:45 +0200 Subject: [PATCH 02/10] $sourcePropertyName can be empty to use the complete source object for setting/population --- docs/usage.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 18e80e1..25f9fd4 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -293,6 +293,10 @@ person.address.populator: Be aware - that both properties have the same name should not lead you think they have the same type. There is really an object conversion behind done by `address.converter`. +If you set the `sourcePropertyName` as empty string the complete `source` object will be used for population. + +Especially in addition wih the `ConvertingPopulator` this is sometimes necessary. + ### ArrayConvertingPopulator If you think that there is no 1:1 relation between `User` and `Address` (or corresponding Person and PersonAddress) From e3228d881b32d446d3f757322d0e64e0b88fd2a1 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Mon, 16 Oct 2023 09:11:12 +0200 Subject: [PATCH 03/10] Update docs/usage.md Co-authored-by: Jacob Dreesen --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 25f9fd4..631fe9b 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -293,7 +293,7 @@ person.address.populator: Be aware - that both properties have the same name should not lead you think they have the same type. There is really an object conversion behind done by `address.converter`. -If you set the `sourcePropertyName` as empty string the complete `source` object will be used for population. +If you specify the `sourcePropertyName` as an empty string, the full `source` object is used for the population. Especially in addition wih the `ConvertingPopulator` this is sometimes necessary. From 9323aadfa85244f767435a77d8f9663459b03706 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Mon, 16 Oct 2023 09:21:01 +0200 Subject: [PATCH 04/10] Update tests/Populator/PropertyMappingPopulatorTest.php Co-authored-by: Jacob Dreesen --- tests/Populator/PropertyMappingPopulatorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Populator/PropertyMappingPopulatorTest.php b/tests/Populator/PropertyMappingPopulatorTest.php index c177545..c81d707 100644 --- a/tests/Populator/PropertyMappingPopulatorTest.php +++ b/tests/Populator/PropertyMappingPopulatorTest.php @@ -107,6 +107,6 @@ public function test_populate_whole_source_object(): void $populator->populate($person, $address); - self::assertEquals('Street', $person->getAddress()->getStreet()); + self::assertSame($address, $person->getAddress()); } } From c6f24b723c2cd66521d94295dcbfe3627d37a934 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 23 Nov 2023 12:16:52 +0100 Subject: [PATCH 05/10] Update docs/usage.md Co-authored-by: Jacob Dreesen --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 631fe9b..449e5f3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -295,7 +295,7 @@ There is really an object conversion behind done by `address.converter`. If you specify the `sourcePropertyName` as an empty string, the full `source` object is used for the population. -Especially in addition wih the `ConvertingPopulator` this is sometimes necessary. +Especially in connection with the `ConvertingPopulator` this is sometimes necessary. ### ArrayConvertingPopulator From 3807bf5941b1a49deeec428f79895e268893ab9a Mon Sep 17 00:00:00 2001 From: "Michael Albrecht (personal)" Date: Thu, 13 Jun 2024 16:13:47 +0200 Subject: [PATCH 06/10] use $this as special sourcePropertyName for converting complete source object (incl. doc) --- docs/usage.md | 130 ++++++++++-------- src/Populator/PropertyMappingPopulator.php | 8 +- .../PropertyMappingPopulatorTest.php | 4 +- 3 files changed, 79 insertions(+), 63 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 449e5f3..7c3035e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -75,7 +75,7 @@ First register the populator as a service: ```yaml # config/services.yaml services: - YourNamespace\PersonNamePopulator: ~ + YourNamespace\PersonNamePopulator: ~ ``` Then declare the following converter in your package config: @@ -83,12 +83,12 @@ Then declare the following converter in your package config: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - populators: - - YourNamespace\PersonNamePopulator - # additional populators may follow + converter: + person.converter: + target: YourNamespace\Person + populators: + - YourNamespace\PersonNamePopulator + # additional populators may follow ``` To put things together, register the populator as services: @@ -96,7 +96,7 @@ To put things together, register the populator as services: ```yaml # config/services.yaml services: - YourNamespace\PersonNamePopulator: ~ + YourNamespace\PersonNamePopulator: ~ ``` And then declare the following converter in your package config: @@ -104,12 +104,12 @@ And then declare the following converter in your package config: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - populators: - - YourNamespace\PersonNamePopulator - # additional populators may follow + converter: + person.converter: + target: YourNamespace\Person + populators: + - YourNamespace\PersonNamePopulator + # additional populators may follow ``` > [!TIP] @@ -131,12 +131,12 @@ You can use it in your converter config via the `properties` keyword: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - properties: - email: ~ - phoneNumber: phone + converter: + person.converter: + target: YourNamespace\Person + properties: + email: ~ + phoneNumber: phone ``` Which will populate @@ -160,12 +160,12 @@ To set a default value for a property, you can use the `default` keyword: # config/packages/neusta_converter.yaml neusta_converter: converter: - person.converter: - properties: - target: YourNamespace\Person - phoneNumber: - source: phone - default: '0123456789' + person.converter: + properties: + target: YourNamespace\Person + phoneNumber: + source: phone + default: '0123456789' ``` The converter will set the value of `phoneNumber` (property of the target object) to `0123456789` if @@ -182,12 +182,12 @@ You can use it in your converter config via the `context` keyword: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - context: - group: ~ - locale: language + converter: + person.converter: + target: YourNamespace\Person + context: + group: ~ + locale: language ``` Which will populate @@ -220,8 +220,9 @@ Conversion done. ## Special Populators -After working a while with the converter pattern, you will notice that many scenarios in the population are very similar. -If the source property can be copied directly to the target property, but only the names of the properties change, +After working a while with the converter pattern, you will notice that many scenarios in the population are very +similar. +If the source property can be copied directly to the target property, but only the names of the properties change, the same populator could be reused over and over again. ### Converting Populator @@ -258,7 +259,7 @@ class Person } ``` -If you have a situation as above and your `User` will have an `Address` which should be populated into `Person`, +If you have a situation as above and your `User` will have an `Address` which should be populated into `Person`, then you have to write a Populator which * gets the `Address` from `User`, @@ -272,22 +273,22 @@ Therefore, we have a `ConvertingPopulator` which can be used as follows: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - # ... - populators: - - person.address.populator + converter: + person.converter: + # ... + populators: + - person.address.populator - address.converter: - # ... + address.converter: + # ... # ... person.address.populator: - class: Neusta\ConverterBundle\Populator\ConvertingPopulator - arguments: - $converter: '@address.converter' - $sourcePropertyName: 'address' - $targetPropertyName: 'address' + class: Neusta\ConverterBundle\Populator\ConvertingPopulator + arguments: + $converter: '@address.converter' + $sourcePropertyName: 'address' + $targetPropertyName: 'address' ``` Be aware - that both properties have the same name should not lead you think they have the same type. @@ -297,9 +298,16 @@ If you specify the `sourcePropertyName` as an empty string, the full `source` ob Especially in connection with the `ConvertingPopulator` this is sometimes necessary. +#### Special case + +In very rare situations it could happen that you want to use the complete source object for population of a special +attribute/property of your target object. In these case you can not define a source property name for the accessor but +you can use `'$this'` and the `ConvertingPopulator` (internally the `PropertyMappingPopulator` will use the object +`$source` itself as value.) + ### ArrayConvertingPopulator -If you think that there is no 1:1 relation between `User` and `Address` (or corresponding Person and PersonAddress) +If you think that there is no 1:1 relation between `User` and `Address` (or corresponding Person and PersonAddress) but a 1:n relation then the `ConvertingPopulator` cannot be used. In these cases we have implemented an extended version of it called `ArrayConvertingPopulator`. @@ -345,26 +353,28 @@ class Person ``` Now you have to declare the following populator: + ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - # ... - populators: - - person.addresses.populator + converter: + person.converter: + # ... + populators: + - person.addresses.populator - address.converter: - # ... + address.converter: + # ... # ... person.addresses.populator: - class: Neusta\ConverterBundle\Populator\ArrayConvertingPopulator - arguments: - $converter: '@address.converter' - $sourcePropertyName: 'addresses' - $targetPropertyName: 'addresses' + class: Neusta\ConverterBundle\Populator\ArrayConvertingPopulator + arguments: + $converter: '@address.converter' + $sourcePropertyName: 'addresses' + $targetPropertyName: 'addresses' ``` + There is no new converter but a different populator implementation for this. ## Context diff --git a/src/Populator/PropertyMappingPopulator.php b/src/Populator/PropertyMappingPopulator.php index 4d4c384..3f586cf 100644 --- a/src/Populator/PropertyMappingPopulator.php +++ b/src/Populator/PropertyMappingPopulator.php @@ -43,7 +43,13 @@ public function __construct( public function populate(object $target, object $source, ?object $ctx = null): void { try { - $value = $this->accessor->getValue($source, $this->sourceProperty) ?? $this->defaultValue; + if ('$this' !== $this->sourceProperty) { + $sourcePropertyValue = $this->accessor->getValue($source, $this->sourceProperty); + } else { + $sourcePropertyValue = $source; + } + + $value = $sourcePropertyValue ?? $this->defaultValue; if (!$this->skipNull || (null !== $value)) { $this->accessor->setValue($target, $this->targetProperty, ($this->mapper)($value, $ctx)); diff --git a/tests/Populator/PropertyMappingPopulatorTest.php b/tests/Populator/PropertyMappingPopulatorTest.php index c81d707..c67812e 100644 --- a/tests/Populator/PropertyMappingPopulatorTest.php +++ b/tests/Populator/PropertyMappingPopulatorTest.php @@ -6,9 +6,9 @@ use Neusta\ConverterBundle\Populator\PropertyMappingPopulator; use Neusta\ConverterBundle\Tests\Fixtures\Model\Source\Address; -use Neusta\ConverterBundle\Tests\Fixtures\Model\PersonAddress; use Neusta\ConverterBundle\Tests\Fixtures\Model\Source\User; use Neusta\ConverterBundle\Tests\Fixtures\Model\Target\Person; +use Neusta\ConverterBundle\Tests\Fixtures\Model\Target\PersonAddress; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; @@ -96,7 +96,7 @@ public function test_populate_skip_null_with_sub_fields_and_null_safety(): void public function test_populate_whole_source_object(): void { - $populator = new PropertyMappingPopulator('address', ''); + $populator = new PropertyMappingPopulator('address', '$this'); $address = (new PersonAddress()) ->setStreet('Street') ->setStreetNo('1') From 4a81971d6f082eeb16d879279afadc25d05d1942 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 13 Jun 2024 16:51:50 +0200 Subject: [PATCH 07/10] Update docs/usage.md Co-authored-by: Jacob Dreesen --- docs/usage.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 7c3035e..29299c5 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -220,8 +220,7 @@ Conversion done. ## Special Populators -After working a while with the converter pattern, you will notice that many scenarios in the population are very -similar. +After working a while with the converter pattern, you will notice that many scenarios in the population are very similar. If the source property can be copied directly to the target property, but only the names of the properties change, the same populator could be reused over and over again. From 891d6d23fbf076ba339228e88b4c88fe9c22e51f Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 13 Jun 2024 16:51:57 +0200 Subject: [PATCH 08/10] Update src/Populator/PropertyMappingPopulator.php Co-authored-by: Jacob Dreesen --- src/Populator/PropertyMappingPopulator.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Populator/PropertyMappingPopulator.php b/src/Populator/PropertyMappingPopulator.php index 3f586cf..53a20d8 100644 --- a/src/Populator/PropertyMappingPopulator.php +++ b/src/Populator/PropertyMappingPopulator.php @@ -43,13 +43,9 @@ public function __construct( public function populate(object $target, object $source, ?object $ctx = null): void { try { - if ('$this' !== $this->sourceProperty) { - $sourcePropertyValue = $this->accessor->getValue($source, $this->sourceProperty); - } else { - $sourcePropertyValue = $source; - } - - $value = $sourcePropertyValue ?? $this->defaultValue; + $value = '$this' !== $this->sourceProperty + ? $this->accessor->getValue($source, $this->sourceProperty) ?? $this->defaultValue + : $source; if (!$this->skipNull || (null !== $value)) { $this->accessor->setValue($target, $this->targetProperty, ($this->mapper)($value, $ctx)); From c355a2a5dd39c6707d5e75e64bb248311822cf86 Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Thu, 13 Jun 2024 17:10:00 +0200 Subject: [PATCH 09/10] Fix indentation --- src/Populator/PropertyMappingPopulator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Populator/PropertyMappingPopulator.php b/src/Populator/PropertyMappingPopulator.php index 53a20d8..73455e9 100644 --- a/src/Populator/PropertyMappingPopulator.php +++ b/src/Populator/PropertyMappingPopulator.php @@ -44,8 +44,8 @@ public function populate(object $target, object $source, ?object $ctx = null): v { try { $value = '$this' !== $this->sourceProperty - ? $this->accessor->getValue($source, $this->sourceProperty) ?? $this->defaultValue - : $source; + ? $this->accessor->getValue($source, $this->sourceProperty) ?? $this->defaultValue + : $source; if (!$this->skipNull || (null !== $value)) { $this->accessor->setValue($target, $this->targetProperty, ($this->mapper)($value, $ctx)); From 0cdd2a61d9c5c8e0a84685b081df1689154fd442 Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Thu, 13 Jun 2024 17:14:46 +0200 Subject: [PATCH 10/10] Revert indentation changes in docs --- docs/usage.md | 114 +++++++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 29299c5..d92ab99 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -75,7 +75,7 @@ First register the populator as a service: ```yaml # config/services.yaml services: - YourNamespace\PersonNamePopulator: ~ + YourNamespace\PersonNamePopulator: ~ ``` Then declare the following converter in your package config: @@ -83,12 +83,12 @@ Then declare the following converter in your package config: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - populators: - - YourNamespace\PersonNamePopulator - # additional populators may follow + converter: + person.converter: + target: YourNamespace\Person + populators: + - YourNamespace\PersonNamePopulator + # additional populators may follow ``` To put things together, register the populator as services: @@ -96,7 +96,7 @@ To put things together, register the populator as services: ```yaml # config/services.yaml services: - YourNamespace\PersonNamePopulator: ~ + YourNamespace\PersonNamePopulator: ~ ``` And then declare the following converter in your package config: @@ -104,12 +104,12 @@ And then declare the following converter in your package config: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - populators: - - YourNamespace\PersonNamePopulator - # additional populators may follow + converter: + person.converter: + target: YourNamespace\Person + populators: + - YourNamespace\PersonNamePopulator + # additional populators may follow ``` > [!TIP] @@ -131,12 +131,12 @@ You can use it in your converter config via the `properties` keyword: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - properties: - email: ~ - phoneNumber: phone + converter: + person.converter: + target: YourNamespace\Person + properties: + email: ~ + phoneNumber: phone ``` Which will populate @@ -159,13 +159,13 @@ To set a default value for a property, you can use the `default` keyword: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - properties: - target: YourNamespace\Person - phoneNumber: - source: phone - default: '0123456789' + converter: + person.converter: + properties: + target: YourNamespace\Person + phoneNumber: + source: phone + default: '0123456789' ``` The converter will set the value of `phoneNumber` (property of the target object) to `0123456789` if @@ -182,12 +182,12 @@ You can use it in your converter config via the `context` keyword: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target: YourNamespace\Person - context: - group: ~ - locale: language + converter: + person.converter: + target: YourNamespace\Person + context: + group: ~ + locale: language ``` Which will populate @@ -272,22 +272,22 @@ Therefore, we have a `ConvertingPopulator` which can be used as follows: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - # ... - populators: - - person.address.populator + converter: + person.converter: + # ... + populators: + - person.address.populator - address.converter: - # ... + address.converter: + # ... # ... person.address.populator: - class: Neusta\ConverterBundle\Populator\ConvertingPopulator - arguments: - $converter: '@address.converter' - $sourcePropertyName: 'address' - $targetPropertyName: 'address' + class: Neusta\ConverterBundle\Populator\ConvertingPopulator + arguments: + $converter: '@address.converter' + $sourcePropertyName: 'address' + $targetPropertyName: 'address' ``` Be aware - that both properties have the same name should not lead you think they have the same type. @@ -356,22 +356,22 @@ Now you have to declare the following populator: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - # ... - populators: - - person.addresses.populator + converter: + person.converter: + # ... + populators: + - person.addresses.populator - address.converter: - # ... + address.converter: + # ... # ... person.addresses.populator: - class: Neusta\ConverterBundle\Populator\ArrayConvertingPopulator - arguments: - $converter: '@address.converter' - $sourcePropertyName: 'addresses' - $targetPropertyName: 'addresses' + class: Neusta\ConverterBundle\Populator\ArrayConvertingPopulator + arguments: + $converter: '@address.converter' + $sourcePropertyName: 'addresses' + $targetPropertyName: 'addresses' ``` There is no new converter but a different populator implementation for this.