diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonDenormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonDenormalizer.php new file mode 100644 index 000000000..69525739e --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonDenormalizer.php @@ -0,0 +1,113 @@ +extractObjectToPopulate($type, $context); + + if (null === $person) { + $person = new Person(); + } + + // Setters applied directly per known field for readability + if (\array_key_exists('firstName', $data)) { + $person->setFirstName($data['firstName']); + } + + if (\array_key_exists('lastName', $data)) { + $person->setLastName($data['lastName']); + } + + if (\array_key_exists('phonenumber', $data)) { + $person->setPhonenumber($this->denormalizer->denormalize($data['phonenumber'], PhoneNumber::class, $format, $context)); + } + + if (\array_key_exists('mobilenumber', $data)) { + $person->setMobilenumber($this->denormalizer->denormalize($data['mobilenumber'], PhoneNumber::class, $format, $context)); + } + + if (\array_key_exists('gender', $data)) { + $gender = $this->denormalizer->denormalize($data['gender'], Gender::class, $format, []); + $person->setGender($gender); + } + + if (\array_key_exists('birthdate', $data)) { + $object = $this->denormalizer->denormalize($data['birthdate'], \DateTime::class, $format, $context); + $person->setBirthdate($object); + } + + if (\array_key_exists('deathdate', $data)) { + $object = $this->denormalizer->denormalize($data['deathdate'], \DateTimeImmutable::class, $format, $context); + $person->setDeathdate($object); + } + + if (\array_key_exists('center', $data)) { + $object = $this->denormalizer->denormalize($data['center'], Center::class, $format, $context); + $person->setCenter($object); + } + + if (\array_key_exists('altNames', $data)) { + foreach ($data['altNames'] as $altName) { + $oldAltName = $person + ->getAltNames() + ->filter(static fn (PersonAltName $n): bool => $n->getKey() === $altName['key'])->first(); + + if (false === $oldAltName) { + $newAltName = new PersonAltName(); + $newAltName->setKey($altName['key']); + $newAltName->setLabel($altName['label']); + $person->addAltName($newAltName); + } else { + $oldAltName->setLabel($altName['label']); + } + } + } + + if (\array_key_exists('email', $data)) { + $person->setEmail($data['email']); + } + + if (\array_key_exists('civility', $data)) { + $civility = $this->denormalizer->denormalize($data['civility'], Civility::class, $format, []); + $person->setCivility($civility); + } + + return $person; + } + + public function supportsDenormalization($data, $type, $format = null): bool + { + return Person::class === $type && 'person' === ($data['type'] ?? null) && !isset($data['id']); + } +} diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php index 88c38cfbb..d19531584 100644 --- a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonNormalizer.php @@ -11,169 +11,31 @@ declare(strict_types=1); namespace Chill\PersonBundle\Serializer\Normalizer; -use Chill\MainBundle\Entity\Center; -use Chill\MainBundle\Entity\Civility; -use Chill\MainBundle\Entity\Gender; use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface; use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\PersonAltName; -use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Repository\ResidentialAddressRepository; use Doctrine\Common\Collections\Collection; -use libphonenumber\PhoneNumber; -use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; -use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; -use Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; -use Symfony\Component\Serializer\Normalizer\ObjectToPopulateTrait; /** * Serialize a Person entity. */ -class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwareInterface, PersonJsonNormalizerInterface +class PersonJsonNormalizer implements NormalizerAwareInterface { - use DenormalizerAwareTrait; - use NormalizerAwareTrait; - use ObjectToPopulateTrait; - public function __construct( private readonly ChillEntityRenderExtension $render, - /* TODO: replace by PersonRenderInterface, as sthis is the only one required */ - private readonly PersonRepository $repository, private readonly CenterResolverManagerInterface $centerResolverManager, private readonly ResidentialAddressRepository $residentialAddressRepository, private readonly PhoneNumberHelperInterface $phoneNumberHelper, ) {} - public function denormalize($data, $type, $format = null, array $context = []) - { - $person = $this->extractObjectToPopulate($type, $context); - - if (\array_key_exists('id', $data) && null === $person) { - $person = $this->repository->find($data['id']); - - if (null === $person) { - throw new UnexpectedValueException("The person with id \"{$data['id']}\" does ".'not exists'); - } - - // currently, not allowed to update a person through api - // if instantiated with id - return $person; - } - - if (null === $person) { - $person = new Person(); - } - - $fields = [ - 'firstName', - 'lastName', - 'phonenumber', - 'mobilenumber', - 'gender', - 'birthdate', - 'deathdate', - 'center', - 'altNames', - 'email', - 'civility', - ]; - - $fields = array_filter( - $fields, - static fn (string $field): bool => \array_key_exists($field, $data) - ); - - foreach ($fields as $item) { - switch ($item) { - case 'firstName': - $person->setFirstName($data[$item]); - - break; - - case 'lastName': - $person->setLastName($data[$item]); - - break; - - case 'phonenumber': - $person->setPhonenumber($this->denormalizer->denormalize($data[$item], PhoneNumber::class, $format, $context)); - - break; - - case 'mobilenumber': - $person->setMobilenumber($this->denormalizer->denormalize($data[$item], PhoneNumber::class, $format, $context)); - - break; - - case 'gender': - $gender = $this->denormalizer->denormalize($data[$item], Gender::class, $format, []); - - $person->setGender($gender); - - break; - - case 'birthdate': - $object = $this->denormalizer->denormalize($data[$item], \DateTime::class, $format, $context); - - $person->setBirthdate($object); - - break; - - case 'deathdate': - $object = $this->denormalizer->denormalize($data[$item], \DateTimeImmutable::class, $format, $context); - - $person->setDeathdate($object); - - break; - - case 'center': - $object = $this->denormalizer->denormalize($data[$item], Center::class, $format, $context); - $person->setCenter($object); - - break; - - case 'altNames': - foreach ($data[$item] as $altName) { - $oldAltName = $person - ->getAltNames() - ->filter(static fn (PersonAltName $n): bool => $n->getKey() === $altName['key'])->first(); - - if (false === $oldAltName) { - $newAltName = new PersonAltName(); - $newAltName->setKey($altName['key']); - $newAltName->setLabel($altName['label']); - $person->addAltName($newAltName); - } else { - $oldAltName->setLabel($altName['label']); - } - } - - break; - - case 'email': - $person->setEmail($data[$item]); - - break; - - case 'civility': - $civility = $this->denormalizer->denormalize($data[$item], Civility::class, $format, []); - - $person->setCivility($civility); - - break; - } - } - - return $person; - } - /** * @param Person $person * @param string|null $format diff --git a/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonReadDenormalizer.php b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonReadDenormalizer.php new file mode 100644 index 000000000..ce8e5ce48 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Serializer/Normalizer/PersonJsonReadDenormalizer.php @@ -0,0 +1,51 @@ +repository->find($data['id']); + + if (null === $person) { + throw new UnexpectedValueException("The person with id \"{$data['id']}\" does ".'not exists'); + } + + return $person; + } + + throw new LogicException(); + } + + public function supportsDenormalization($data, string $type, ?string $format = null) + { + return is_array($data) && Person::class === $type && 'person' === ($data['type'] ?? null) && isset($data['id']); + } +} diff --git a/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/PersonJsonDenormalizerTest.php b/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/PersonJsonDenormalizerTest.php new file mode 100644 index 000000000..b18f17995 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Tests/Serializer/Normalizer/PersonJsonDenormalizerTest.php @@ -0,0 +1,21 @@ +getMockBuilder(PersonRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $denormalizer = new PersonJsonReadDenormalizer($repository); + + $data = [ + 'type' => 'person', + 'id' => 123, + ]; + + self::assertTrue($denormalizer->supportsDenormalization($data, Person::class)); + } + + public function testSupportsDenormalizationReturnsFalseForInvalidData(): void + { + $repository = $this->getMockBuilder(PersonRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $denormalizer = new PersonJsonReadDenormalizer($repository); + + // not an array + self::assertFalse($denormalizer->supportsDenormalization('not-an-array', Person::class)); + + // missing type + self::assertFalse($denormalizer->supportsDenormalization(['id' => 1], Person::class)); + + // wrong type value + self::assertFalse($denormalizer->supportsDenormalization(['type' => 'not-person', 'id' => 1], Person::class)); + + // missing id + self::assertFalse($denormalizer->supportsDenormalization(['type' => 'person'], Person::class)); + + // wrong target class + self::assertFalse($denormalizer->supportsDenormalization(['type' => 'person', 'id' => 1], \stdClass::class)); + } + + public function testDenormalizeReturnsPersonFromRepository(): void + { + $person = new Person(); + + $repository = $this->getMockBuilder(PersonRepository::class) + ->disableOriginalConstructor() + ->onlyMethods(['find']) + ->getMock(); + + $repository->expects(self::once()) + ->method('find') + ->with(123) + ->willReturn($person); + + $denormalizer = new PersonJsonReadDenormalizer($repository); + + $result = $denormalizer->denormalize(['id' => 123], Person::class); + + self::assertSame($person, $result); + } +}