mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-10-21 12:42:51 +00:00
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:
@@ -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 = [];
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
{
|
||||
|
@@ -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'));
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
@@ -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']);
|
||||
|
@@ -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([
|
||||
|
@@ -32,6 +32,7 @@ class PersonAltNameType extends AbstractType
|
||||
[
|
||||
'label' => $label,
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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 %}
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -11,3 +11,6 @@ services:
|
||||
|
||||
Chill\PersonBundle\Actions\Remove\Handler\:
|
||||
resource: '../../Actions/Remove/Handler'
|
||||
|
||||
Chill\PersonBundle\Actions\PersonEdit\Service\:
|
||||
resource: '../../Actions/PersonEdit/Service'
|
||||
|
Reference in New Issue
Block a user