From 11d23c1bb74178bc2e74a55f9926aa5e0214d2ae Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 3 Aug 2023 10:06:44 +0200 Subject: [PATCH 1/7] documentation for ConvertingPopulator --- docs/usage.md | 109 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 92 insertions(+), 17 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 318a315..b4a0ded 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -98,8 +98,8 @@ To put things together register the factory and populator as services: ```yaml # config/services.yaml services: - YourNamespace\PersonFactory: ~ - YourNamespace\PersonNamePopulator: ~ + YourNamespace\PersonFactory: ~ + YourNamespace\PersonNamePopulator: ~ ``` And then declare the following converter in your package config: @@ -107,12 +107,12 @@ And then declare the following converter in your package config: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - target_factory: YourNamespace\PersonFactory - populators: - - YourNamespace\PersonNamePopulator - # additional populators may follow + converter: + person.converter: + target_factory: YourNamespace\PersonFactory + populators: + - YourNamespace\PersonNamePopulator + # additional populators may follow ``` > Note: You can use a custom implementation of the `Converter` interface via the `converter` keyword. @@ -129,23 +129,23 @@ You can use it in your converter config via the `properties` keyword: ```yaml # config/packages/neusta_converter.yaml neusta_converter: - converter: - person.converter: - ... - properties: - email: ~ - phoneNumber: phone + converter: + person.converter: + ... + properties: + email: ~ + phoneNumber: phone ``` -Which will populate +Which will populate -`email` (property of the target object) +`email` (property of the target object) with `email` (property of the source object) and -`phoneNumber` (property of the target object) +`phoneNumber` (property of the target object) with `phone` (property of the source object). @@ -197,6 +197,81 @@ $person = $this->converter->convert($user); Conversion done. +## Special Populators + +After a while you will recognize that a lot of scenarios in population are very similiar to each other. Some of them +could be done with the same populator except the target and the source property name. + +### Converting Populator +Let's go on with the following extended model classes: +```php +class Address +{ + private string $street; + private string $number; + private string $postalCode; + private string $city; + // ... +} + +class User +{ + // ... + private Address $address; + // ... +} +``` + +and the target type is `Person`: + +```php +class PersonAddress +{ + private string $streetWithNumber; + private string $postalCodeAndCity; + // ... +} + +class Person +{ + // ... + private PersonAddress $address; + // ... +} +``` + +If you have a situation as above and your User will have an address which should be populated into Person than you have +to write a Populator which +* gets the address from User, +* converts it into a PersonAddress object +* and sets it in Person. + +The second step is typically a task for a (e.g. Address-) Converter. + +Therefore we have a ConvertingPopulator which can easily be used: +```yaml +# config/packages/neusta_converter.yaml +neusta_converter: + converter: + person.converter: + ... + populators: + - person.address.populator + + address.converter: + ... + +... + person.address.populator: + 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. +There is really an object conversion behind done by `address.converter`. + ## Context Sometimes you will need parameterized conversion which is not depending on the objects themselves. From 6c949af1091b23b431b9124cf64729ef311ebcf1 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 3 Aug 2023 14:47:29 +0200 Subject: [PATCH 2/7] new ArrayConvertingPopulator --- docs/usage.md | 28 ++++++--- src/Populator/ArrayConvertingPopulator.php | 53 +++++++++++++++++ .../ArrayPropertyMappingPopulator.php | 59 +++++++++++++++++++ src/Populator/PropertyMappingPopulator.php | 4 +- 4 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 src/Populator/ArrayConvertingPopulator.php create mode 100644 src/Populator/ArrayPropertyMappingPopulator.php diff --git a/docs/usage.md b/docs/usage.md index b4a0ded..682ff82 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -203,7 +203,9 @@ After a while you will recognize that a lot of scenarios in population are very could be done with the same populator except the target and the source property name. ### Converting Populator + Let's go on with the following extended model classes: + ```php class Address { @@ -241,14 +243,16 @@ class Person ``` If you have a situation as above and your User will have an address which should be populated into Person than you have -to write a Populator which -* gets the address from User, +to write a Populator which + +* gets the address from User, * converts it into a PersonAddress object * and sets it in Person. The second step is typically a task for a (e.g. Address-) Converter. Therefore we have a ConvertingPopulator which can easily be used: + ```yaml # config/packages/neusta_converter.yaml neusta_converter: @@ -262,16 +266,24 @@ neusta_converter: ... ... - person.address.populator: - class: Neusta\ConverterBundle\Populator\ConvertingPopulator - arguments: - $converter: '@address.converter' - $sourcePropertyName: 'address' - $targetPropertyName: 'address' +person.address.populator: + 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. There is really an object conversion behind done by `address.converter`. +### ArrayConvertingPopulator + +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 can not be used. + +In these cases we have implemented an extended version of it called `ArrayConvertingPopulator`. + ## Context Sometimes you will need parameterized conversion which is not depending on the objects themselves. diff --git a/src/Populator/ArrayConvertingPopulator.php b/src/Populator/ArrayConvertingPopulator.php new file mode 100644 index 0000000..77e61af --- /dev/null +++ b/src/Populator/ArrayConvertingPopulator.php @@ -0,0 +1,53 @@ + from TSource + * into an object of type array for a field of TTarget. + * + * @template TSource of object + * @template TTarget of object + * @template TContext of object|null + * + * @implements Populator + */ +final class ArrayConvertingPopulator implements Populator +{ + private PropertyMappingPopulator $populator; + + /** + * @template TInnerSource of object + * @template TInnerTarget of object + * + * @param Converter $converter + */ + public function __construct( + Converter $converter, + string $sourcePropertyName, + string $targetPropertyName, + PropertyAccessorInterface $accessor = null, + ) { + $this->populator = new ArrayPropertyMappingPopulator( + $targetPropertyName, + $sourcePropertyName, + \Closure::fromCallable([$converter, 'convert']), + $accessor, + ); + } + + /** + * @throws PopulationException + */ + public function populate(object $target, object $source, ?object $ctx = null): void + { + $this->populator->populate($target, $source, $ctx); + } +} diff --git a/src/Populator/ArrayPropertyMappingPopulator.php b/src/Populator/ArrayPropertyMappingPopulator.php new file mode 100644 index 0000000..2ab0f2f --- /dev/null +++ b/src/Populator/ArrayPropertyMappingPopulator.php @@ -0,0 +1,59 @@ + + */ +final class ArrayPropertyMappingPopulator implements Populator +{ + /** @var \Closure(mixed, TContext=):mixed */ + private \Closure $mapper; + private PropertyAccessorInterface $accessor; + + /** + * @param \Closure(mixed, TContext=):mixed|null $mapper + */ + public function __construct( + private string $targetProperty, + private string $sourceProperty, + ?\Closure $mapper = null, + PropertyAccessorInterface $accessor = null, + ) + { + $this->mapper = $mapper ?? static fn($v) => $v; + $this->accessor = $accessor ?? PropertyAccess::createPropertyAccessor(); + } + + /** + * @throws PopulationException + */ + public function populate(object $target, object $source, ?object $ctx = null): void + { + try { + $this->accessor->setValue( + $target, + $this->targetProperty, + array_map( + function ($item) use ($ctx) { + return ($this->mapper)($item, $ctx); + }, + $this->accessor->getValue($source, $this->sourceProperty) + ) + ); + } catch (\Throwable $exception) { + throw new PopulationException($this->sourceProperty, $this->targetProperty, $exception); + } + } +} diff --git a/src/Populator/PropertyMappingPopulator.php b/src/Populator/PropertyMappingPopulator.php index c488d69..36c2e90 100644 --- a/src/Populator/PropertyMappingPopulator.php +++ b/src/Populator/PropertyMappingPopulator.php @@ -41,7 +41,9 @@ public function __construct( public function populate(object $target, object $source, ?object $ctx = null): void { try { - $this->accessor->setValue($target, $this->targetProperty, + $this->accessor->setValue( + $target, + $this->targetProperty, ($this->mapper)($this->accessor->getValue($source, $this->sourceProperty), $ctx), ); } catch (\Throwable $exception) { From 7723184df8ea5568efae8f718fe6c84941dc4cfd Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 3 Aug 2023 14:52:27 +0200 Subject: [PATCH 3/7] some doc for it --- docs/usage.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 682ff82..477f269 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -284,6 +284,73 @@ n relation then the ConvertingPopulator can not be used. In these cases we have implemented an extended version of it called `ArrayConvertingPopulator`. +This populator uses the same internal technique but expects to convert eac item of a source array of properties before +it will be set into the target object. + +#### Example: User to Person + +So imagine the addresses will now be an array of addresses (billing address, shipping addresses, contact +addresses, ...). + +```php +class Address +{ + private string $street; + private string $number; + private string $postalCode; + private string $city; + // ... +} + +class User +{ + // ... + private Address[] $addresses; + // ... +} +``` + +and the target type is `Person`: + +```php +class PersonAddress +{ + private string $streetWithNumber; + private string $postalCodeAndCity; + // ... +} + +class Person +{ + // ... + private PersonAddress[] $addresses; + // ... +} +``` + +Now you have to declare the following populator: +```yaml +# config/packages/neusta_converter.yaml +neusta_converter: + converter: + person.converter: + ... + populators: + - person.addresses.populator + + address.converter: + ... + +... +person.addresses.populator: + 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 Sometimes you will need parameterized conversion which is not depending on the objects themselves. From bd09aeb303c16f5e932e5e105730de0c2a2eadf3 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Thu, 3 Aug 2023 14:53:02 +0200 Subject: [PATCH 4/7] fix typo --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 477f269..7e66711 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -349,7 +349,7 @@ person.addresses.populator: $sourcePropertyName: 'addresses' $targetPropertyName: 'addresses' ``` -there is no new converter but a different populator implementation for this. +There is no new converter but a different populator implementation for this. ## Context From 6f8fafeb7253abeeb2e9af31972782782e84a0da Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Tue, 8 Aug 2023 17:50:47 +0200 Subject: [PATCH 5/7] introduce correct array item accessor handling for ArrayConvertingPopulator and ArrayPropertyMappingPopulator --- config/services.yaml | 8 +++ src/Populator/ArrayConvertingPopulator.php | 14 ++-- .../ArrayPropertyMappingPopulator.php | 22 +++++- tests/Fixtures/Model/ContactNumber.php | 29 ++++++++ tests/Fixtures/Model/ContactNumberFactory.php | 19 ++++++ tests/Fixtures/Model/Hobby.php | 53 +++++++++++++++ tests/Fixtures/Model/Person.php | 67 ++++++++++++++++++- tests/Fixtures/Model/Phone.php | 48 +++++++++++++ tests/Fixtures/Model/User.php | 65 +++++++++++++++++- ...rrayConvertingPopulatorIntegrationTest.php | 51 ++++++++++++++ ...ropertyMappingPopulatorIntegrationTest.php | 45 +++++++++++++ .../ArrayPropertyMappingPopulatorTest.php | 49 ++++++++++++++ .../app/config/packages/neusta_converter.yaml | 5 ++ tests/app/config/services.yaml | 18 +++++ 14 files changed, 482 insertions(+), 11 deletions(-) create mode 100644 tests/Fixtures/Model/ContactNumber.php create mode 100644 tests/Fixtures/Model/ContactNumberFactory.php create mode 100644 tests/Fixtures/Model/Hobby.php create mode 100644 tests/Fixtures/Model/Phone.php create mode 100644 tests/Populator/ArrayConvertingPopulatorIntegrationTest.php create mode 100644 tests/Populator/ArrayPropertyMappingPopulatorIntegrationTest.php create mode 100644 tests/Populator/ArrayPropertyMappingPopulatorTest.php diff --git a/config/services.yaml b/config/services.yaml index 1d4d79b..0eb1f1f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -27,3 +27,11 @@ services: neusta_converter.converting_populator: abstract: true class: Neusta\ConverterBundle\Populator\ConvertingPopulator + + neusta_converter.array_property_mapping_populator: + abstract: true + class: Neusta\ConverterBundle\Populator\ArrayPropertyMappingPopulator + + neusta_converter.array_converting_populator: + abstract: true + class: Neusta\ConverterBundle\Populator\ArrayConvertingPopulator diff --git a/src/Populator/ArrayConvertingPopulator.php b/src/Populator/ArrayConvertingPopulator.php index 77e61af..8c8ac0f 100644 --- a/src/Populator/ArrayConvertingPopulator.php +++ b/src/Populator/ArrayConvertingPopulator.php @@ -21,7 +21,7 @@ */ final class ArrayConvertingPopulator implements Populator { - private PropertyMappingPopulator $populator; + private ArrayPropertyMappingPopulator $populator; /** * @template TInnerSource of object @@ -30,15 +30,19 @@ final class ArrayConvertingPopulator implements Populator * @param Converter $converter */ public function __construct( - Converter $converter, - string $sourcePropertyName, - string $targetPropertyName, + Converter $converter, + string $sourceArrayPropertyName, + string $sourceArrayItemPropertyName, + string $targetPropertyName, + PropertyAccessorInterface $itemAccessor = null, PropertyAccessorInterface $accessor = null, ) { $this->populator = new ArrayPropertyMappingPopulator( $targetPropertyName, - $sourcePropertyName, + $sourceArrayPropertyName, + $sourceArrayItemPropertyName, \Closure::fromCallable([$converter, 'convert']), + $itemAccessor, $accessor, ); } diff --git a/src/Populator/ArrayPropertyMappingPopulator.php b/src/Populator/ArrayPropertyMappingPopulator.php index 2ab0f2f..d6e0b59 100644 --- a/src/Populator/ArrayPropertyMappingPopulator.php +++ b/src/Populator/ArrayPropertyMappingPopulator.php @@ -20,6 +20,7 @@ final class ArrayPropertyMappingPopulator implements Populator { /** @var \Closure(mixed, TContext=):mixed */ private \Closure $mapper; + private PropertyAccessorInterface $itemAccessor; private PropertyAccessorInterface $accessor; /** @@ -27,12 +28,15 @@ final class ArrayPropertyMappingPopulator implements Populator */ public function __construct( private string $targetProperty, - private string $sourceProperty, + private string $sourceArrayProperty, + private ?string $sourceArrayItemProperty = null, ?\Closure $mapper = null, + PropertyAccessorInterface $itemAccessor = null, PropertyAccessorInterface $accessor = null, ) { $this->mapper = $mapper ?? static fn($v) => $v; + $this->itemAccessor = $itemAccessor ?? PropertyAccess::createPropertyAccessor(); $this->accessor = $accessor ?? PropertyAccess::createPropertyAccessor(); } @@ -42,6 +46,17 @@ public function __construct( public function populate(object $target, object $source, ?object $ctx = null): void { try { + $unwrappedArray = array_map( + function ($arrayItem) { + if (!empty($this->sourceArrayItemProperty)) { + return $this->itemAccessor->getValue($arrayItem, $this->sourceArrayItemProperty); + } + return $arrayItem; + }, + $this->accessor->getValue($source, $this->sourceArrayProperty) + ); + + $this->accessor->setValue( $target, $this->targetProperty, @@ -49,11 +64,12 @@ public function populate(object $target, object $source, ?object $ctx = null): v function ($item) use ($ctx) { return ($this->mapper)($item, $ctx); }, - $this->accessor->getValue($source, $this->sourceProperty) + $unwrappedArray ) ); } catch (\Throwable $exception) { - throw new PopulationException($this->sourceProperty, $this->targetProperty, $exception); + throw new PopulationException($this->sourceArrayProperty, $this->targetProperty, $exception); } } + } diff --git a/tests/Fixtures/Model/ContactNumber.php b/tests/Fixtures/Model/ContactNumber.php new file mode 100644 index 0000000..1b0ae76 --- /dev/null +++ b/tests/Fixtures/Model/ContactNumber.php @@ -0,0 +1,29 @@ +phoneNumber; + } + + /** + * @param string $phoneNumber + * @return ContactNumber + */ + public function setPhoneNumber(string $phoneNumber): ContactNumber + { + $this->phoneNumber = $phoneNumber; + return $this; + } + +} diff --git a/tests/Fixtures/Model/ContactNumberFactory.php b/tests/Fixtures/Model/ContactNumberFactory.php new file mode 100644 index 0000000..e7dbeaf --- /dev/null +++ b/tests/Fixtures/Model/ContactNumberFactory.php @@ -0,0 +1,19 @@ + + */ +class ContactNumberFactory implements TargetFactory +{ + public function create(?object $ctx = null): ContactNumber + { + return new ContactNumber(); + } +} diff --git a/tests/Fixtures/Model/Hobby.php b/tests/Fixtures/Model/Hobby.php new file mode 100644 index 0000000..23ac666 --- /dev/null +++ b/tests/Fixtures/Model/Hobby.php @@ -0,0 +1,53 @@ +label; + } + + /** + * @param string $label + * @return Hobby + */ + public function setLabel(string $label): Hobby + { + $this->label = $label; + return $this; + } + + /** + * @return string + */ + public function getCategory(): string + { + return $this->category; + } + + /** + * @param string $category + * @return Hobby + */ + public function setCategory(string $category): Hobby + { + $this->category = $category; + return $this; + } + + public function __toString(): string + { + return $this->label . ' hobby_as_string'; + } +} diff --git a/tests/Fixtures/Model/Person.php b/tests/Fixtures/Model/Person.php index ac96ca4..ded0b51 100644 --- a/tests/Fixtures/Model/Person.php +++ b/tests/Fixtures/Model/Person.php @@ -16,11 +16,37 @@ class Person private ?PersonAddress $address = null; - public function getFullName(): ?string + /** @var array */ + private array $favouriteMovies; + + /** @var array */ + private array $activities; + + /** @var array */ + private array $contactNumbers; + + /** + * @return array + */ + public function getContactNumbers(): array { - return $this->fullName; + return $this->contactNumbers; } + /** + * @param array $contactNumbers + * @return Person + */ + public function setContactNumbers(array $contactNumbers): Person + { + $this->contactNumbers = $contactNumbers; + return $this; + } + + public function getFullName(): string + { + return $this->fullName; + } public function setFullName(?string $fullName): Person { $this->fullName = $fullName; @@ -49,6 +75,43 @@ public function setAddress(?PersonAddress $address): Person return $this; } + /** + * @return array + */ + public function getFavouriteMovies(): array + { + return $this->favouriteMovies; + } + + /** + * @param array $favouriteMovies + * @return Person + */ + public function setFavouriteMovies(array $favouriteMovies): Person + { + $this->favouriteMovies = $favouriteMovies; + return $this; + } + + /** + * @return array + */ + public function getActivities(): array + { + return $this->activities; + } + + /** + * @param array $activities + * @return Person + */ + public function setActivities(array $activities): Person + { + $this->activities = $activities; + return $this; + } + + public function getLocale(): ?string { return $this->locale; diff --git a/tests/Fixtures/Model/Phone.php b/tests/Fixtures/Model/Phone.php new file mode 100644 index 0000000..8c07fd8 --- /dev/null +++ b/tests/Fixtures/Model/Phone.php @@ -0,0 +1,48 @@ +type; + } + + /** + * @param string $type + * @return Phone + */ + public function setType(string $type): Phone + { + $this->type = $type; + return $this; + } + + /** + * @return string + */ + public function getNumber(): string + { + return $this->number; + } + + /** + * @param string $number + * @return Phone + */ + public function setNumber(string $number): Phone + { + $this->number = $number; + return $this; + } + +} diff --git a/tests/Fixtures/Model/User.php b/tests/Fixtures/Model/User.php index d6bb445..c9a31f5 100644 --- a/tests/Fixtures/Model/User.php +++ b/tests/Fixtures/Model/User.php @@ -13,6 +13,15 @@ class User private int $ageInYears; private Address $address; + /** @var array */ + private array $favouriteMovies; + + /** @var array */ + private array $hobbies; + + /** @var array */ + private array $phones; + private UnknownType $fieldWithUnknownType; public function getUuid(): int @@ -84,14 +93,68 @@ public function setAddress(Address $address): User return $this; } + /** + * @return array + */ + public function getFavouriteMovies(): array + { + return $this->favouriteMovies; + } + + /** + * @param array $favouriteMovies + * @return User + */ + public function setFavouriteMovies(array $favouriteMovies): User + { + $this->favouriteMovies = $favouriteMovies; + return $this; + } + + /** + * @return array + */ + public function getHobbies(): array + { + return $this->hobbies; + } + + /** + * @param array $hobbies + * @return User + */ + public function setHobbies(array $hobbies): User + { + $this->hobbies = $hobbies; + return $this; + } + public function getFieldWithUnknownType(): UnknownType { return $this->fieldWithUnknownType; } - public function setFieldWithUnknownType(UnknownType $fieldWithUnknownType): User { $this->fieldWithUnknownType = $fieldWithUnknownType; return $this; } + + /** + * @return array + */ + public function getPhones(): array + { + return $this->phones; + } + + /** + * @param array $phones + * @return User + */ + public function setPhones(array $phones): User + { + $this->phones = $phones; + return $this; + } + } diff --git a/tests/Populator/ArrayConvertingPopulatorIntegrationTest.php b/tests/Populator/ArrayConvertingPopulatorIntegrationTest.php new file mode 100644 index 0000000..5192c4f --- /dev/null +++ b/tests/Populator/ArrayConvertingPopulatorIntegrationTest.php @@ -0,0 +1,51 @@ +populator = self::getContainer()->get('test.person.contactnumbers.populator'); + } + + public function testPopulate(): void + { + $phone1 = (new Phone())->setType('mobile')->setNumber('0171 2456543'); + $phone2 = (new Phone())->setType('mobile')->setNumber('0172 2456543'); + $phone3 = (new Phone())->setType('home')->setNumber('0421 2456543'); + + $user = (new User())->setPhones([$phone1, $phone2, $phone3,]); + $person = new Person(); + + $this->populator->populate($person, $user); + + self::assertEquals( + [ + '0171 2456543', + '0172 2456543', + '0421 2456543', + ], + array_map( + function ($item) { + return $item->getPhoneNumber(); + }, + $person->getContactNumbers() + ) + ); + } +} diff --git a/tests/Populator/ArrayPropertyMappingPopulatorIntegrationTest.php b/tests/Populator/ArrayPropertyMappingPopulatorIntegrationTest.php new file mode 100644 index 0000000..b9571f6 --- /dev/null +++ b/tests/Populator/ArrayPropertyMappingPopulatorIntegrationTest.php @@ -0,0 +1,45 @@ +populator = self::getContainer()->get('test.person.activities.populator'); + } + + public function testPopulate(): void + { + $user = (new User())->setHobbies([ + (new Hobby())->setLabel('reading'), + (new Hobby())->setLabel('swimming'), + (new Hobby())->setLabel('computers'), + ]); + $person = new Person(); + + $this->populator->populate($person, $user); + + self::assertEquals( + [ + 'reading', + 'swimming', + 'computers', + ], + $person->getActivities() + ); + } +} diff --git a/tests/Populator/ArrayPropertyMappingPopulatorTest.php b/tests/Populator/ArrayPropertyMappingPopulatorTest.php new file mode 100644 index 0000000..3e995f1 --- /dev/null +++ b/tests/Populator/ArrayPropertyMappingPopulatorTest.php @@ -0,0 +1,49 @@ +setFavouriteMovies([ + 'Once upon a time in the West', + 'Once upon a time in America', + ]); + $person = new Person(); + + $populator->populate($person, $user); + + self::assertEquals([ + 'Once upon a time in the West', + 'Once upon a time in America', + ], $person->getFavouriteMovies()); + } + + public function test_populateWithInnerProperty(): void + { + $populator = new ArrayPropertyMappingPopulator('activities', 'hobbies','label'); + $user = (new User())->setHobbies([ + (new Hobby())->setLabel('reading'), + (new Hobby())->setLabel('swimming'), + (new Hobby())->setLabel('computers'), + ]); + $person = new Person(); + + $populator->populate($person, $user); + + self::assertEquals(['reading', 'swimming', 'computers'], $person->getActivities()); + } +} diff --git a/tests/app/config/packages/neusta_converter.yaml b/tests/app/config/packages/neusta_converter.yaml index 43b1b36..868a0af 100644 --- a/tests/app/config/packages/neusta_converter.yaml +++ b/tests/app/config/packages/neusta_converter.yaml @@ -11,3 +11,8 @@ neusta_converter: # context: # group: ~ # same property name # locale: language # different property names + + test.contactnumber.converter: + target_factory: Neusta\ConverterBundle\Tests\Fixtures\Model\ContactNumberFactory + properties: + phoneNumber: number diff --git a/tests/app/config/services.yaml b/tests/app/config/services.yaml index a6dff5e..6dc1eda 100644 --- a/tests/app/config/services.yaml +++ b/tests/app/config/services.yaml @@ -48,5 +48,23 @@ services: $targetProperty: 'fullName' $sourceProperty: 'fullName' + test.person.activities.populator: + parent: 'neusta_converter.array_property_mapping_populator' + public: true + arguments: + $sourceArrayItemProperty: 'label' + $sourceArrayProperty: 'hobbies' + $targetProperty: 'activities' + + test.person.contactnumbers.populator: + parent: 'neusta_converter.array_converting_populator' + public: true + arguments: + $converter: '@test.contactnumber.converter' + $sourceArrayPropertyName: 'phones' + $sourceArrayItemPropertyName: '' + $targetPropertyName: 'contactNumbers' + Neusta\ConverterBundle\Tests\Fixtures\Model\PersonFactory: ~ Neusta\ConverterBundle\Tests\Fixtures\Model\AddressFactory: ~ + Neusta\ConverterBundle\Tests\Fixtures\Model\ContactNumberFactory: ~ From 493e4321a48c6016aa7f8b0c669bc96fbf59a243 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Tue, 15 Aug 2023 13:30:11 +0200 Subject: [PATCH 6/7] correct incorrect tests --- src/Populator/ArrayConvertingPopulator.php | 5 +++-- tests/app/config/services.yaml | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Populator/ArrayConvertingPopulator.php b/src/Populator/ArrayConvertingPopulator.php index 8c8ac0f..901de73 100644 --- a/src/Populator/ArrayConvertingPopulator.php +++ b/src/Populator/ArrayConvertingPopulator.php @@ -32,11 +32,12 @@ final class ArrayConvertingPopulator implements Populator public function __construct( Converter $converter, string $sourceArrayPropertyName, - string $sourceArrayItemPropertyName, string $targetPropertyName, + ?string $sourceArrayItemPropertyName = null, PropertyAccessorInterface $itemAccessor = null, PropertyAccessorInterface $accessor = null, - ) { + ) + { $this->populator = new ArrayPropertyMappingPopulator( $targetPropertyName, $sourceArrayPropertyName, diff --git a/tests/app/config/services.yaml b/tests/app/config/services.yaml index 6dc1eda..e7498cc 100644 --- a/tests/app/config/services.yaml +++ b/tests/app/config/services.yaml @@ -62,7 +62,6 @@ services: arguments: $converter: '@test.contactnumber.converter' $sourceArrayPropertyName: 'phones' - $sourceArrayItemPropertyName: '' $targetPropertyName: 'contactNumbers' Neusta\ConverterBundle\Tests\Fixtures\Model\PersonFactory: ~ From 01c110901f678b4ee1ab9eb62cccf3cc8164be02 Mon Sep 17 00:00:00 2001 From: Michael Albrecht Date: Tue, 15 Aug 2023 13:46:35 +0200 Subject: [PATCH 7/7] declare null property mapper because of PHPSTAN 8.0 --- src/DependencyInjection/NeustaConverterExtension.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DependencyInjection/NeustaConverterExtension.php b/src/DependencyInjection/NeustaConverterExtension.php index 84380b7..33ff02c 100644 --- a/src/DependencyInjection/NeustaConverterExtension.php +++ b/src/DependencyInjection/NeustaConverterExtension.php @@ -39,6 +39,7 @@ private function registerConverterConfiguration(string $id, array $config, Conta ->setArguments([ '$targetProperty' => $targetProperty, '$sourceProperty' => $sourceProperty ?? $targetProperty, + '$mapper' => null, '$accessor' => new Reference('property_accessor'), ]); } @@ -49,6 +50,7 @@ private function registerConverterConfiguration(string $id, array $config, Conta ->setArguments([ '$targetProperty' => $targetProperty, '$sourceProperty' => $sourceProperty ?? $targetProperty, + '$mapper' => null, '$accessor' => new Reference('property_accessor'), ]); }