Refactor PersonEdit flow to introduce PersonEditDTO

- Replaced `Person` entity binding with `PersonEditDTO` in `PersonType` to decouple data transfer and entity manipulation.
- Added `PersonEditDTOFactory` for creating and mapping `PersonEditDTO` instances.
- Simplified `PersonAltNameDataMapper` and `PersonIdentifiersDataMapper`.
- Updated `PersonEditController` to use `PersonEditDTO` for better separation of concerns.
- Adjusted related tests, configurations, and templates accordingly.
This commit is contained in:
2025-10-21 13:22:04 +02:00
parent 532f2dd842
commit e9e6c05e3d
13 changed files with 439 additions and 98 deletions

View File

@@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Actions\PersonEdit;
use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Gender;
use Chill\MainBundle\Entity\Language;
use Chill\PersonBundle\Entity\AdministrativeStatus;
use Chill\PersonBundle\Entity\EmploymentStatus;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\MaritalStatus;
use Chill\PersonBundle\Entity\PersonAltName;
use Chill\PersonBundle\Validator\Constraints\Person\Birthdate;
use Doctrine\Common\Collections\Collection;
use libphonenumber\PhoneNumber;
use Misd\PhoneNumberBundle\Validator\Constraints\PhoneNumber as MisdPhoneNumberConstraint;
use Symfony\Component\Validator\Constraints as Assert;
class PersonEditDTO
{
#[Assert\NotBlank(message: 'The firstname cannot be empty')]
#[Assert\Length(max: 255)]
public string $firstName;
#[Assert\NotBlank(message: 'The lastname cannot be empty')]
#[Assert\Length(max: 255)]
public string $lastName;
#[Birthdate]
public ?\DateTime $birthdate = null;
#[Assert\GreaterThanOrEqual(propertyPath: 'birthdate')]
#[Assert\LessThanOrEqual('today')]
public ?\DateTimeImmutable $deathdate = null;
#[Assert\NotNull(message: 'The gender must be set')]
public ?Gender $gender = null;
#[Assert\Valid]
public CommentEmbeddable $genderComment;
public ?int $numberOfChildren = null;
/**
* @var array<string, PersonAltName> where the key is the altname's key
*/
public array $altNames = [];
public string $memo = '';
public ?EmploymentStatus $employmentStatus = null;
public ?AdministrativeStatus $administrativeStatus = null;
public string $placeOfBirth = '';
public ?string $contactInfo = '';
#[MisdPhoneNumberConstraint(type: [MisdPhoneNumberConstraint::FIXED_LINE, MisdPhoneNumberConstraint::VOIP, MisdPhoneNumberConstraint::PERSONAL_NUMBER])]
public ?PhoneNumber $phonenumber = null;
#[MisdPhoneNumberConstraint(type: [MisdPhoneNumberConstraint::MOBILE])]
public ?PhoneNumber $mobilenumber = null;
public ?bool $acceptSms = false;
#[Assert\Valid(traverse: true)]
public Collection $otherPhonenumbers; // Collection<int, \Chill\PersonBundle\Entity\PersonPhone>
#[Assert\Email]
public ?string $email = '';
public ?bool $acceptEmail = false;
public ?Country $countryOfBirth = null;
public ?Country $nationality = null;
public Collection $spokenLanguages; // Collection<int, Language>
public ?Civility $civility = null;
public ?MaritalStatus $maritalStatus = null;
public ?\DateTimeInterface $maritalStatusDate = null;
#[Assert\Valid]
public CommentEmbeddable $maritalStatusComment;
public ?array $cFData = null;
/**
* @var array<string, PersonIdentifier>
*/
#[Assert\Valid(traverse: true)]
public array $identifiers = [];
}

View File

@@ -0,0 +1,130 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\PersonBundle\Actions\PersonEdit\Service;
use Chill\PersonBundle\Actions\PersonEdit\PersonEditDTO;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
class PersonEditDTOFactory
{
public function __construct(
private readonly ConfigPersonAltNamesHelper $configPersonAltNamesHelper,
private readonly PersonIdentifierManagerInterface $personIdentifierManager,
) {}
public function createPersonEditDTO(Person $person): PersonEditDTO
{
$dto = new PersonEditDTO();
$dto->firstName = $person->getFirstName();
$dto->lastName = $person->getLastName();
$dto->birthdate = $person->getBirthdate();
$dto->deathdate = (null !== $deathDate = $person->getDeathdate()) ? \DateTimeImmutable::createFromInterface($deathDate) : null;
$dto->gender = $person->getGender();
$dto->genderComment = $person->getGenderComment();
$dto->numberOfChildren = $person->getNumberOfChildren();
$dto->memo = $person->getMemo() ?? '';
$dto->employmentStatus = $person->getEmploymentStatus();
$dto->administrativeStatus = $person->getAdministrativeStatus();
$dto->placeOfBirth = $person->getPlaceOfBirth() ?? '';
$dto->contactInfo = $person->getcontactInfo();
$dto->phonenumber = $person->getPhonenumber();
$dto->mobilenumber = $person->getMobilenumber();
$dto->acceptSms = $person->getAcceptSMS();
$dto->otherPhonenumbers = $person->getOtherPhoneNumbers();
$dto->email = $person->getEmail();
$dto->acceptEmail = $person->getAcceptEmail();
$dto->countryOfBirth = $person->getCountryOfBirth();
$dto->nationality = $person->getNationality();
$dto->spokenLanguages = $person->getSpokenLanguages();
$dto->civility = $person->getCivility();
$dto->maritalStatus = $person->getMaritalStatus();
$dto->maritalStatusDate = $person->getMaritalStatusDate();
$dto->maritalStatusComment = $person->getMaritalStatusComment();
$dto->cFData = $person->getCFData();
foreach ($this->configPersonAltNamesHelper->getChoices() as $key => $labels) {
$altName = $person->getAltNames()->findFirst(fn (int $k, PersonAltName $altName) => $altName->getKey() === $key);
if (null === $altName) {
$altName = new PersonAltName();
$altName->setKey($key);
}
$dto->altNames[$key] = $altName;
}
foreach ($this->personIdentifierManager->getWorkers() as $worker) {
$identifier = $person
->getIdentifiers()
->findFirst(fn (int $key, PersonIdentifier $identifier) => $worker->getDefinition() === $identifier->getDefinition());
if (null === $identifier) {
$identifier = new PersonIdentifier($worker->getDefinition());
$person->addIdentifier($identifier);
}
$dto->identifiers['identifier_'.$worker->getDefinition()->getId()] = $identifier;
}
return $dto;
}
public function mapPersonEditDTOtoPerson(PersonEditDTO $personEditDTO, Person $person): void
{
// Copy all editable fields from the DTO back to the Person entity
$person
->setFirstName($personEditDTO->firstName)
->setLastName($personEditDTO->lastName)
->setBirthdate($personEditDTO->birthdate)
->setDeathdate($personEditDTO->deathdate)
->setGender($personEditDTO->gender)
->setGenderComment($personEditDTO->genderComment)
->setNumberOfChildren($personEditDTO->numberOfChildren)
->setMemo($personEditDTO->memo)
->setEmploymentStatus($personEditDTO->employmentStatus)
->setAdministrativeStatus($personEditDTO->administrativeStatus)
->setPlaceOfBirth($personEditDTO->placeOfBirth)
->setcontactInfo($personEditDTO->contactInfo)
->setPhonenumber($personEditDTO->phonenumber)
->setMobilenumber($personEditDTO->mobilenumber)
->setAcceptSMS($personEditDTO->acceptSms ?? false)
->setOtherPhoneNumbers($personEditDTO->otherPhonenumbers)
->setEmail($personEditDTO->email)
->setAcceptEmail($personEditDTO->acceptEmail ?? false)
->setCountryOfBirth($personEditDTO->countryOfBirth)
->setNationality($personEditDTO->nationality)
->setSpokenLanguages($personEditDTO->spokenLanguages)
->setCivility($personEditDTO->civility)
->setMaritalStatus($personEditDTO->maritalStatus)
->setMaritalStatusDate($personEditDTO->maritalStatusDate)
->setMaritalStatusComment($personEditDTO->maritalStatusComment)
->setCFData($personEditDTO->cFData);
foreach ($personEditDTO->altNames as $altName) {
if ('' === $altName->getLabel()) {
$person->removeAltName($altName);
} else {
$person->addAltName($altName);
}
}
foreach ($personEditDTO->identifiers as $identifier) {
$worker = $this->personIdentifierManager->buildWorkerByPersonIdentifierDefinition($identifier->getDefinition());
if ($worker->isEmpty($identifier)) {
$person->removeIdentifier($identifier);
} else {
$person->addIdentifier($identifier);
}
}
}
}

View File

@@ -28,6 +28,8 @@ class ConfigPersonAltNamesHelper
/**
* get the choices as key => values.
*
* @return array<string, array<string, string>> where the key is the altName's key, and the value is an array of TranslatableString
*/
public function getChoices(): array
{

View File

@@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\CustomFieldsBundle\EntityRepository\CustomFieldsDefaultGroupRepository;
use Chill\PersonBundle\Actions\PersonEdit\Service\PersonEditDTOFactory;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Form\PersonType;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
@@ -38,6 +39,7 @@ final readonly class PersonEditController
private EntityManagerInterface $entityManager,
private UrlGeneratorInterface $urlGenerator,
private Environment $twig,
private PersonEditDTOFactory $personEditDTOFactory,
) {}
/**
@@ -50,9 +52,11 @@ final readonly class PersonEditController
throw new AccessDeniedHttpException('You are not allowed to edit this person.');
}
$dto = $this->personEditDTOFactory->createPersonEditDTO($person);
$form = $this->formFactory->create(
PersonType::class,
$person,
$dto,
['cFGroup' => $this->customFieldsDefaultGroupRepository->findOneByEntity(Person::class)?->getCustomFieldsGroup()]
);
@@ -62,6 +66,7 @@ final readonly class PersonEditController
$session
->getFlashBag()->add('error', new TranslatableMessage('This form contains errors'));
} elseif ($form->isSubmitted() && $form->isValid()) {
$this->personEditDTOFactory->mapPersonEditDTOtoPerson($dto, $person);
$this->entityManager->flush();
$session->getFlashBag()->add('success', new TranslatableMessage('The person data has been updated'));

View File

@@ -12,10 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Form\DataMapper;
use Chill\PersonBundle\Entity\PersonAltName;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
class PersonAltNameDataMapper implements DataMapperInterface
{
@@ -25,62 +22,24 @@ class PersonAltNameDataMapper implements DataMapperInterface
return;
}
if (!$viewData instanceof Collection) {
throw new UnexpectedTypeException($viewData, Collection::class);
}
$mapIndexToKey = [];
foreach ($viewData->getIterator() as $key => $altName) {
/* @var PersonAltName $altName */
$mapIndexToKey[$altName->getKey()] = $key;
if (!is_array($viewData)) {
throw new \InvalidArgumentException('View data must be an array');
}
foreach ($forms as $key => $form) {
if (\array_key_exists($key, $mapIndexToKey)) {
$form->setData($viewData->get($mapIndexToKey[$key])->getLabel());
$personAltName = $viewData[$key];
if (!$personAltName instanceof PersonAltName) {
throw new \InvalidArgumentException('PersonAltName must be an instance of PersonAltName');
}
$form->setData($personAltName->getLabel());
}
}
public function mapFormsToData(\Traversable $forms, &$viewData): void
{
$mapIndexToKey = [];
if (\is_array($viewData)) {
$dataIterator = $viewData;
} else {
$dataIterator = $viewData instanceof ArrayCollection ?
$viewData->toArray() : $viewData->getIterator();
}
foreach ($dataIterator as $key => $altName) {
/* @var PersonAltName $altName */
$mapIndexToKey[$altName->getKey()] = $key;
}
foreach ($forms as $key => $form) {
$isEmpty = empty($form->getData());
if (\array_key_exists($key, $mapIndexToKey)) {
if ($isEmpty) {
$viewData->remove($mapIndexToKey[$key]);
} else {
$viewData->get($mapIndexToKey[$key])->setLabel($form->getData());
}
} else {
if (!$isEmpty) {
$altName = (new PersonAltName())
->setKey($key)
->setLabel($form->getData());
if (\is_array($viewData)) {
$viewData[] = $altName;
} else {
$viewData->add($altName);
}
}
}
$personAltName = array_find($viewData, fn (PersonAltName $altName) => $altName->getKey() === $key);
$personAltName->setLabel($form->getData());
}
}
}

View File

@@ -14,65 +14,51 @@ namespace Chill\PersonBundle\Form\DataMapper;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\PersonIdentifier\Exception\UnexpectedTypeException;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierDefinitionRepository;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\FormInterface;
final readonly class PersonIdentifiersDataMapper implements DataMapperInterface
{
public function __construct(
private PersonIdentifierManagerInterface $identifierManager,
private PersonIdentifierDefinitionRepository $identifierDefinitionRepository,
) {}
/**
* @pure
*/
public function mapDataToForms($viewData, \Traversable $forms): void
{
if (!$viewData instanceof Collection) {
throw new UnexpectedTypeException($viewData, Collection::class);
if (!$viewData instanceof PersonIdentifier) {
throw new UnexpectedTypeException($viewData, PersonIdentifier::class);
}
/** @var array<string, FormInterface> $formsByKey */
$formsByKey = iterator_to_array($forms);
foreach ($this->identifierManager->getWorkers() as $worker) {
if (!$worker->getDefinition()->isEditableByUsers()) {
continue;
}
$form = $formsByKey['identifier_'.$worker->getDefinition()->getId()] ?? null;
if (null === $form) {
continue;
}
$identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $worker->getDefinition() === $identifier->getDefinition());
if (null === $identifier) {
$identifier = new PersonIdentifier($worker->getDefinition());
}
$form->setData($identifier->getValue());
$worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($viewData->getDefinition());
if (!$worker->getDefinition()->isEditableByUsers()) {
return;
}
foreach ($forms as $key => $form) {
$form->setData($viewData->getValue()[$key]);
}
}
/**
* @pure
*/
public function mapFormsToData(\Traversable $forms, &$viewData): void
{
if (!$viewData instanceof Collection) {
throw new UnexpectedTypeException($viewData, Collection::class);
if (!$viewData instanceof PersonIdentifier) {
throw new UnexpectedTypeException($viewData, PersonIdentifier::class);
}
$worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($viewData->getDefinition());
if (!$worker->getDefinition()->isEditableByUsers()) {
return;
}
$values = [];
foreach ($forms as $name => $form) {
$identifierId = (int) substr((string) $name, 11);
$identifier = $viewData->findFirst(fn (int $key, PersonIdentifier $identifier) => $identifier->getDefinition()->getId() === $identifierId);
$definition = $this->identifierDefinitionRepository->find($identifierId);
if (null === $identifier) {
$identifier = new PersonIdentifier($definition);
$viewData->add($identifier);
}
if (!$identifier->getDefinition()->isEditableByUsers()) {
continue;
}
$worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($definition);
$identifier->setValue($form->getData());
$identifier->setCanonical($worker->canonicalizeValue($identifier->getValue()));
$values[$name] = $form->getData();
}
$viewData->setValue($values);
$viewData->setCanonical($worker->canonicalizeValue($viewData->getValue()));
}
}

View File

@@ -29,7 +29,7 @@ final class PersonIdentifiersType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
foreach ($this->identifierManager->getWorkers() as $worker) {
foreach ($this->identifierManager->getWorkers() as $k => $worker) {
if (!$worker->getDefinition()->isEditableByUsers()) {
continue;
}
@@ -45,16 +45,16 @@ final class PersonIdentifiersType extends AbstractType
options: [
'compound' => true,
'label' => $this->translatableStringHelper->localize($worker->getDefinition()->getLabel()),
'error_bubbling' => false,
]
);
$subBuilder->setDataMapper($this->identifiersDataMapper);
$worker->buildForm($subBuilder);
$builder->add($subBuilder);
}
$builder->setDataMapper($this->identifiersDataMapper);
}
public function configureOptions(OptionsResolver $resolver)
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('step', 'on_edit')
->setAllowedValues('step', ['on_edit', 'on_create']);

View File

@@ -21,8 +21,8 @@ use Chill\MainBundle\Form\Type\PickCivilityType;
use Chill\MainBundle\Form\Type\Select2CountryType;
use Chill\MainBundle\Form\Type\Select2LanguageType;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Actions\PersonEdit\PersonEditDTO;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonPhone;
use Chill\PersonBundle\Form\Type\PersonAltNameType;
use Chill\PersonBundle\Form\Type\PersonPhoneType;
@@ -242,7 +242,7 @@ class PersonType extends AbstractType
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Person::class,
'data_class' => PersonEditDTO::class,
]);
$resolver->setRequired([

View File

@@ -32,6 +32,7 @@ class PersonAltNameType extends AbstractType
[
'label' => $label,
'required' => false,
'empty_data' => '',
]
);
}

View File

@@ -19,6 +19,9 @@ interface PersonIdentifierEngineInterface
{
public static function getName(): string;
/**
* @phpstan-pure
*/
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string;
public function buildForm(FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void;

View File

@@ -140,9 +140,7 @@
<fieldset>
<legend><h2>{{ 'person.Identifiers'|trans }}</h2></legend>
<div>
{% for f in form.identifiers %}
{{ form_row(f) }}
{% endfor %}
{{ form_widget(form.identifiers) }}
</div>
</fieldset>
{% else %}

View File

@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Action\PersonEdit\Service;
use Chill\MainBundle\Entity\Civility;
use Chill\MainBundle\Entity\Country;
use Chill\MainBundle\Entity\Embeddable\CommentEmbeddable;
use Chill\MainBundle\Entity\Gender;
use Chill\MainBundle\Entity\Language;
use Chill\PersonBundle\Actions\PersonEdit\PersonEditDTO;
use Chill\PersonBundle\Actions\PersonEdit\Service\PersonEditDTOFactory;
use Chill\PersonBundle\Config\ConfigPersonAltNamesHelper;
use Chill\PersonBundle\Entity\AdministrativeStatus;
use Chill\PersonBundle\Entity\EmploymentStatus;
use Chill\PersonBundle\Entity\MaritalStatus;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\PersonAltName;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Doctrine\Common\Collections\ArrayCollection;
use libphonenumber\PhoneNumber;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
*
* @coversNothing
*/
class PersonEditDTOFactoryTest extends TestCase
{
use ProphecyTrait;
public function testMapPersonEditDTOtoPersonCopiesAllFields(): void
{
$configHelper = $this->createMock(ConfigPersonAltNamesHelper::class);
$identifierManager = $this->createMock(PersonIdentifierManagerInterface::class);
$factory = new PersonEditDTOFactory($configHelper, $identifierManager);
$dto = new PersonEditDTO();
$dto->firstName = 'John';
$dto->lastName = 'Doe';
$dto->birthdate = new \DateTime('1980-05-10');
$dto->deathdate = new \DateTimeImmutable('2050-01-01');
$dto->gender = new Gender();
$dto->genderComment = new CommentEmbeddable('gender comment');
$dto->numberOfChildren = 2;
$dto->memo = 'Some memo';
$dto->employmentStatus = new EmploymentStatus();
$dto->administrativeStatus = new AdministrativeStatus();
$dto->placeOfBirth = 'Cityville';
$dto->contactInfo = 'Some contact info';
$phone = new PhoneNumber();
$dto->phonenumber = $phone;
$mobile = new PhoneNumber();
$dto->mobilenumber = $mobile;
$dto->acceptSms = true;
$dto->otherPhonenumbers = new ArrayCollection();
$dto->email = 'john.doe@example.org';
$dto->acceptEmail = true;
$dto->countryOfBirth = new Country();
$dto->nationality = new Country();
$dto->spokenLanguages = new ArrayCollection([new Language()]);
$dto->civility = new Civility();
$dto->maritalStatus = new MaritalStatus();
$dto->maritalStatusDate = new \DateTime('2010-01-01');
$dto->maritalStatusComment = new CommentEmbeddable('married');
$dto->cFData = ['foo' => 'bar'];
$person = new Person();
$factory->mapPersonEditDTOtoPerson($dto, $person);
self::assertSame('John', $person->getFirstName());
self::assertSame('Doe', $person->getLastName());
self::assertSame($dto->birthdate, $person->getBirthdate());
self::assertSame($dto->deathdate, $person->getDeathdate());
self::assertSame($dto->gender, $person->getGender());
self::assertSame($dto->genderComment, $person->getGenderComment());
self::assertSame($dto->numberOfChildren, $person->getNumberOfChildren());
self::assertSame('Some memo', $person->getMemo());
self::assertSame($dto->employmentStatus, $person->getEmploymentStatus());
self::assertSame($dto->administrativeStatus, $person->getAdministrativeStatus());
self::assertSame('Cityville', $person->getPlaceOfBirth());
self::assertSame('Some contact info', $person->getcontactInfo());
self::assertSame($phone, $person->getPhonenumber());
self::assertSame($mobile, $person->getMobilenumber());
self::assertTrue($person->getAcceptSMS());
self::assertSame($dto->otherPhonenumbers, $person->getOtherPhoneNumbers());
self::assertSame('john.doe@example.org', $person->getEmail());
self::assertTrue($person->getAcceptEmail());
self::assertSame($dto->countryOfBirth, $person->getCountryOfBirth());
self::assertSame($dto->nationality, $person->getNationality());
self::assertSame($dto->spokenLanguages, $person->getSpokenLanguages());
self::assertSame($dto->civility, $person->getCivility());
self::assertSame($dto->maritalStatus, $person->getMaritalStatus());
self::assertSame($dto->maritalStatusDate, $person->getMaritalStatusDate());
self::assertSame($dto->maritalStatusComment, $person->getMaritalStatusComment());
self::assertSame($dto->cFData, $person->getCFData());
}
public function testAltNamesHandlingWithConfigHelper(): void
{
$configHelper = $this->createMock(ConfigPersonAltNamesHelper::class);
$configHelper->method('getChoices')->willReturn([
'aka' => ['en' => 'Also Known As'],
'nickname' => ['en' => 'Nickname'],
]);
$identifierManager = $this->createMock(PersonIdentifierManagerInterface::class);
$identifierManager->method('getWorkers')->willReturn([]);
$factory = new PersonEditDTOFactory($configHelper, $identifierManager);
$person = new Person();
$dto = $factory->createPersonEditDTO($person);
// Assert DTO has two altNames keys from helper
self::assertCount(2, $dto->altNames);
self::assertContainsOnlyInstancesOf(PersonAltName::class, $dto->altNames);
self::assertSame(['aka', 'nickname'], array_keys($dto->altNames));
self::assertSame(['aka' => 'aka', 'nickname' => 'nickname'], array_map(fn (PersonAltName $altName) => $altName->getKey(), $dto->altNames));
// Fill only one label and leave the other empty
$dto->altNames['aka']->setLabel('The Boss');
// 'nickname' remains empty by default
// Map DTO back to person
$factory->mapPersonEditDTOtoPerson($dto, $person);
// Assert only the filled alt name is persisted on the person
$altNames = $person->getAltNames();
self::assertCount(1, $altNames);
$altNameArray = $altNames->toArray();
self::assertSame('aka', $altNameArray[0]->getKey());
self::assertSame('The Boss', $altNameArray[0]->getLabel());
}
}

View File

@@ -11,3 +11,6 @@ services:
Chill\PersonBundle\Actions\Remove\Handler\:
resource: '../../Actions/Remove/Handler'
Chill\PersonBundle\Actions\PersonEdit\Service\:
resource: '../../Actions/PersonEdit/Service'