From 7eea357cbe97da2e7353b81473781cb81f400c5c Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 13 Jun 2024 17:19:39 +0200 Subject: [PATCH] `'$this'` as `$sourcePropertyName` means complete source object (#60) Co-authored-by: Jacob Dreesen --- docs/usage.md | 33 +++++++++++++------ src/Populator/PropertyMappingPopulator.php | 4 ++- .../PropertyMappingPopulatorTest.php | 19 ++++++++++- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 18e80e1..d92ab99 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -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 @@ -221,7 +221,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. -If the source property can be copied directly to the target property, but only the names of the properties change, +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 +258,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`, @@ -293,9 +293,20 @@ 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 specify the `sourcePropertyName` as an empty string, the full `source` object is used for the population. + +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`. @@ -341,6 +352,7 @@ class Person ``` Now you have to declare the following populator: + ```yaml # config/packages/neusta_converter.yaml neusta_converter: @@ -361,6 +373,7 @@ person.addresses.populator: $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..73455e9 100644 --- a/src/Populator/PropertyMappingPopulator.php +++ b/src/Populator/PropertyMappingPopulator.php @@ -43,7 +43,9 @@ public function __construct( public function populate(object $target, object $source, ?object $ctx = null): void { try { - $value = $this->accessor->getValue($source, $this->sourceProperty) ?? $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)); diff --git a/tests/Populator/PropertyMappingPopulatorTest.php b/tests/Populator/PropertyMappingPopulatorTest.php index 57af128..c67812e 100644 --- a/tests/Populator/PropertyMappingPopulatorTest.php +++ b/tests/Populator/PropertyMappingPopulatorTest.php @@ -8,6 +8,7 @@ use Neusta\ConverterBundle\Tests\Fixtures\Model\Source\Address; 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; @@ -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', '$this'); + $address = (new PersonAddress()) + ->setStreet('Street') + ->setStreetNo('1') + ->setCity('Capitol City') + ->setPostalCode('12345'); + + $person = new Person(); + + $populator->populate($person, $address); + + self::assertSame($address, $person->getAddress()); + } }