From f4c3997e55d1580ac1ee94a97277b74ed5ecf012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Wed, 26 Oct 2022 13:30:16 +0200 Subject: [PATCH] DX: Refactor ListPerson to extract methods linked to label and select --- .../Export/Export/ListPerson.php | 383 +--------------- .../Export/Helper/ListPersonHelper.php | 428 ++++++++++++++++++ 2 files changed, 443 insertions(+), 368 deletions(-) create mode 100644 src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php diff --git a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php index 631e3b69a..eb746e122 100644 --- a/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php +++ b/src/Bundle/ChillPersonBundle/Export/Export/ListPerson.php @@ -20,28 +20,20 @@ use Chill\MainBundle\Export\GroupedExportInterface; use Chill\MainBundle\Export\Helper\ExportAddressHelper; use Chill\MainBundle\Export\ListInterface; use Chill\MainBundle\Form\Type\ChillDateType; -use Chill\MainBundle\Repository\CivilityRepositoryInterface; -use Chill\MainBundle\Repository\CountryRepository; -use Chill\MainBundle\Repository\LanguageRepositoryInterface; -use Chill\MainBundle\Repository\UserRepositoryInterface; use Chill\MainBundle\Templating\TranslatableStringHelper; use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Export\Declarations; -use Chill\PersonBundle\Repository\MaritalStatusRepositoryInterface; +use Chill\PersonBundle\Export\Helper\ListPersonHelper; use Chill\PersonBundle\Security\Authorization\PersonVoter; -use DateTime; use DateTimeImmutable; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Query; -use Doctrine\ORM\QueryBuilder; -use Exception; use PhpOffice\PhpSpreadsheet\Shared\Date; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Contracts\Translation\TranslatorInterface; use function addcslashes; use function array_key_exists; use function array_keys; @@ -56,96 +48,35 @@ use function uniqid; */ class ListPerson implements ExportElementValidatedInterface, ListInterface, GroupedExportInterface { - public const FIELDS = [ - 'id', - 'civility', - 'firstName', - 'lastName', - 'birthdate', - 'center', - 'deathdate', - 'placeOfBirth', - 'gender', - 'genderComment', - 'maritalStatus', - 'maritalStatusComment', - 'maritalStatusDate', - 'memo', - 'email', - 'phonenumber', - 'mobilenumber', - 'numberOfChildren', - 'contactInfo', - 'countryOfBirth', - 'nationality', - // add full addresses - 'address_fields', - // add a list of spoken languages - 'spokenLanguages', - // add household id - 'household_id', - // add created at, created by, updated at, and updated by - 'lifecycleUpdate', - ]; - - private const HELPER_ATTRIBUTES = - ExportAddressHelper::F_ATTRIBUTES | - ExportAddressHelper::F_BUILDING | - ExportAddressHelper::F_COUNTRY | - ExportAddressHelper::F_GEOM | - ExportAddressHelper::F_POSTAL_CODE | - ExportAddressHelper::F_STREET | - ExportAddressHelper::F_AS_STRING; - private ExportAddressHelper $addressHelper; - private CivilityRepositoryInterface $civilityRepository; - - private CountryRepository $countryRepository; - private CustomFieldProvider $customFieldProvider; private EntityManagerInterface $entityManager; - private LanguageRepositoryInterface $languageRepository; - - private MaritalStatusRepositoryInterface $maritalStatusRepository; + private ListPersonHelper $listPersonHelper; private $slugs = []; private TranslatableStringHelper $translatableStringHelper; - private TranslatorInterface $translator; - - private UserRepositoryInterface $userRepository; - public function __construct( ExportAddressHelper $addressHelper, - CivilityRepositoryInterface $civilityRepository, - CountryRepository $countryRepository, CustomFieldProvider $customFieldProvider, + ListPersonHelper $listPersonHelper, EntityManagerInterface $em, - LanguageRepositoryInterface $languageRepository, - MaritalStatusRepositoryInterface $maritalStatusRepository, - TranslatableStringHelper $translatableStringHelper, - TranslatorInterface $translator, - UserRepositoryInterface $userRepository + TranslatableStringHelper $translatableStringHelper ) { $this->addressHelper = $addressHelper; - $this->civilityRepository = $civilityRepository; - $this->countryRepository = $countryRepository; - $this->entityManager = $em; - $this->languageRepository = $languageRepository; - $this->maritalStatusRepository = $maritalStatusRepository; - $this->translator = $translator; - $this->translatableStringHelper = $translatableStringHelper; $this->customFieldProvider = $customFieldProvider; - $this->userRepository = $userRepository; + $this->listPersonHelper = $listPersonHelper; + $this->entityManager = $em; + $this->translatableStringHelper = $translatableStringHelper; } public function buildForm(FormBuilderInterface $builder) { - $choices = array_combine(self::FIELDS, self::FIELDS); + $choices = array_combine(ListPersonHelper::FIELDS, ListPersonHelper::FIELDS); foreach ($this->getCustomFields() as $cf) { $choices[$this->translatableStringHelper->localize($cf->getName())] @@ -205,183 +136,24 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou public function getLabels($key, array $values, $data) { - if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + if (in_array($key, $this->listPersonHelper->getAllPossibleFields(), true)) { + return $this->listPersonHelper->getLabels($key, $values, $data); } - switch ($key) { - case 'birthdate': - case 'deathdate': - case 'maritalStatusDate': - case 'createdAt': - case 'updatedAt': - // for birthdate, we have to transform the string into a date - // to format the date correctly. - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - // warning: won't work with DateTimeImmutable as we reset time a few lines later - $date = DateTime::createFromFormat('Y-m-d', $value); - $hasTime = false; - - if (false === $date) { - $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); - $hasTime = true; - } - - // check that the creation could occurs. - if (false === $date) { - throw new Exception(sprintf('The value %s could ' - . 'not be converted to %s', $value, DateTime::class)); - } - - if (!$hasTime) { - $date->setTime(0, 0, 0); - } - - return $date; - }; - - case 'createdBy': - case 'updatedBy': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - return $this->userRepository->find($value)->getLabel(); - }; - - case 'civility': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $civility = $this->civilityRepository->find($value); - - if (null === $civility) { - return ''; - } - - return $this->translatableStringHelper->localize($civility->getName()); - }; - - case 'gender': - // for gender, we have to translate men/women statement - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $this->translator->trans($value); - }; - - case 'maritalStatus': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $maritalStatus = $this->maritalStatusRepository->find($value); - - return $this->translatableStringHelper->localize($maritalStatus->getName()); - }; - - case 'spokenLanguages': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $ids = json_decode($value); - - return - implode( - '|', - array_map(function ($id) { - if (null === $id) { - return ''; - } - - $lang = $this->languageRepository->find($id); - - if (null === $lang) { - return null; - } - - return $this->translatableStringHelper->localize($lang->getName()); - }, $ids) - ); - }; - - case 'countryOfBirth': - case 'nationality': - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - if (null === $value) { - return ''; - } - - $country = $this->countryRepository->find($value); - - return $this->translatableStringHelper->localize( - $country->getName() - ); - }; - - default: - // for fields which are associated with person - if (in_array($key, self::FIELDS, true)) { - return function ($value) use ($key) { - if ('_header' === $value) { - return $this->translator->trans($key); - } - - return $value; - }; - } - - return $this->getLabelForCustomField($key, $values, $data); - } + return $this->getLabelForCustomField($key, $values, $data); } public function getQueryKeys($data) { $fields = []; - foreach (self::FIELDS as $key) { + foreach (ListPersonHelper::FIELDS as $key) { if (!in_array($key, $data['fields'], true)) { continue; } if (substr($key, 0, strlen('address_fields')) === 'address_fields') { - $fields = array_merge($fields, $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields')); + $fields = array_merge($fields, $this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields')); continue; } @@ -442,109 +214,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou $fields = $data['fields']; - foreach (self::FIELDS as $f) { - if (!in_array($f, $fields, true)) { - continue; - } - - switch ($f) { - case 'countryOfBirth': - case 'nationality': - $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); - - break; - - case 'address_fields': - foreach ($this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') as $key) { - $qb - ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); - } - - $this->addCurrentAddressAt($qb, $data['address_date']); - - break; - - case 'spokenLanguages': - $qb - ->leftJoin('person.spokenLanguages', 'spokenLanguage') - ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') - ->addGroupBy('person'); - - if (in_array('center', $fields, true)) { - $qb->addGroupBy('center'); - } - - if (in_array('address_fields', $fields, true)) { - $qb->addGroupBy('address_fieldsid'); - } - - if (in_array('household_id', $fields, true)) { - $qb->addGroupBy('household_id'); - } - - break; - - case 'household_id': - $qb - ->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id'); - - $this->addCurrentAddressAt($qb, $data['address_date']); - - break; - - case 'center': - $qb - ->addSelect('IDENTITY(centerHistory.center) AS center') - ->leftJoin('person.centerHistory', 'centerHistory') - ->andWhere( - $qb->expr()->orX( - $qb->expr()->isNull('centerHistory'), - $qb->expr()->andX( - $qb->expr()->lte('centerHistory.startDate', ':address_date'), - $qb->expr()->orX( - $qb->expr()->isNull('centerHistory.endDate'), - $qb->expr()->gte('centerHistory.endDate', ':address_date') - ) - ) - ) - ) - ->setParameter('address_date', $data['address_date']); - - break; - - case 'lifecycleUpdate': - $qb - ->addSelect('person.createdAt AS createdAt') - ->addSelect('IDENTITY(person.createdBy) AS createdBy') - ->addSelect('person.updatedAt AS updatedAt') - ->addSelect('IDENTITY(person.updatedBy) AS updatedBy'); - - break; - - case 'genderComment': - $qb->addSelect('person.genderComment.comment AS genderComment'); - - break; - - case 'maritalStatus': - $qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus'); - - break; - - case 'maritalStatusComment': - $qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment'); - - break; - - case 'civility': - $qb->addSelect('IDENTITY(person.civility) AS civility'); - - break; - - default: - $qb->addSelect(sprintf('person.%s as %s', $f, $f)); - } - } + $this->listPersonHelper->addSelect($qb, $fields, $data['address_date']); foreach ($this->getCustomFields() as $cf) { if (!in_array($cf->getSlug(), $fields, true)) { @@ -595,7 +265,7 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou { // get the field starting with address_ $addressFields = array_filter( - self::FIELDS, + ListPersonHelper::FIELDS, static fn (string $el): bool => substr($el, 0, 8) === 'address_' ); @@ -611,29 +281,6 @@ class ListPerson implements ExportElementValidatedInterface, ListInterface, Grou } } - private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void - { - if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) { - $qb - ->leftJoin('person.householdAddresses', 'personHouseholdAddress') - ->andWhere( - $qb->expr()->orX( - // no address at this time - $qb->expr()->isNull('personHouseholdAddress'), - // there is one address... - $qb->expr()->andX( - $qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'), - $qb->expr()->orX( - $qb->expr()->isNull('personHouseholdAddress.validTo'), - $qb->expr()->gt('personHouseholdAddress.validTo', ':address_date') - ) - ) - ) - ) - ->setParameter('address_date', $date); - } - } - private function DQLToSlug($cleanedSlug) { return $this->slugs[$cleanedSlug]['slug']; diff --git a/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php new file mode 100644 index 000000000..7a14e0446 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Export/Helper/ListPersonHelper.php @@ -0,0 +1,428 @@ +addressHelper = $addressHelper; + $this->civilityRepository = $civilityRepository; + $this->countryRepository = $countryRepository; + $this->languageRepository = $languageRepository; + $this->maritalStatusRepository = $maritalStatusRepository; + $this->translatableStringHelper = $translatableStringHelper; + $this->translator = $translator; + $this->userRepository = $userRepository; + } + + /** + * @param array|value-of[] $fields + */ + public function addSelect(QueryBuilder $qb, array $fields, DateTimeImmutable $computedDate): void + { + foreach (ListPersonHelper::FIELDS as $f) { + if (!in_array($f, $fields, true)) { + continue; + } + + switch ($f) { + case 'countryOfBirth': + case 'nationality': + $qb->addSelect(sprintf('IDENTITY(person.%s) as %s', $f, $f)); + + break; + + case 'address_fields': + foreach ($this->addressHelper->getKeys(ListPersonHelper::HELPER_ATTRIBUTES, 'address_fields') as $key) { + $qb + ->addSelect(sprintf('IDENTITY(personHouseholdAddress.address) AS %s', $key)); + } + + $this->addCurrentAddressAt($qb, $computedDate); + + break; + + case 'spokenLanguages': + $qb + ->leftJoin('person.spokenLanguages', 'spokenLanguage') + ->addSelect('AGGREGATE(spokenLanguage.id) AS spokenLanguages') + ->addGroupBy('person'); + + if (in_array('center', $fields, true)) { + $qb->addGroupBy('center'); + } + + if (in_array('address_fields', $fields, true)) { + $qb->addGroupBy('address_fieldsid'); + } + + if (in_array('household_id', $fields, true)) { + $qb->addGroupBy('household_id'); + } + + break; + + case 'household_id': + $qb + ->addSelect('IDENTITY(personHouseholdAddress.household) AS household_id'); + + $this->addCurrentAddressAt($qb, $computedDate); + + break; + + case 'center': + $qb + ->addSelect('IDENTITY(centerHistory.center) AS center') + ->leftJoin('person.centerHistory', 'centerHistory') + ->andWhere( + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory'), + $qb->expr()->andX( + $qb->expr()->lte('centerHistory.startDate', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('centerHistory.endDate'), + $qb->expr()->gte('centerHistory.endDate', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $computedDate); + + break; + + case 'lifecycleUpdate': + $qb + ->addSelect('person.createdAt AS createdAt') + ->addSelect('IDENTITY(person.createdBy) AS createdBy') + ->addSelect('person.updatedAt AS updatedAt') + ->addSelect('IDENTITY(person.updatedBy) AS updatedBy'); + + break; + + case 'genderComment': + $qb->addSelect('person.genderComment.comment AS genderComment'); + + break; + + case 'maritalStatus': + $qb->addSelect('IDENTITY(person.maritalStatus) AS maritalStatus'); + + break; + + case 'maritalStatusComment': + $qb->addSelect('person.maritalStatusComment.comment AS maritalStatusComment'); + + break; + + case 'civility': + $qb->addSelect('IDENTITY(person.civility) AS civility'); + + break; + + default: + $qb->addSelect(sprintf('person.%s as %s', $f, $f)); + } + } + } + + /** + * @return array|string[] + */ + public function getAllPossibleFields(): array + { + return array_merge( + self::FIELDS, + ['createdAt', 'createdBy', 'updatedAt', 'updatedBy'], + $this->addressHelper->getKeys(self::HELPER_ATTRIBUTES, 'address_fields') + ); + } + + public function getLabels($key, array $values, $data): callable + { + if (substr($key, 0, strlen('address_fields')) === 'address_fields') { + return $this->addressHelper->getLabel($key, $values, $data, 'address_fields'); + } + + switch ($key) { + case 'birthdate': + case 'deathdate': + case 'maritalStatusDate': + case 'createdAt': + case 'updatedAt': + // for birthdate, we have to transform the string into a date + // to format the date correctly. + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + // warning: won't work with DateTimeImmutable as we reset time a few lines later + $date = DateTime::createFromFormat('Y-m-d', $value); + $hasTime = false; + + if (false === $date) { + $date = DateTime::createFromFormat('Y-m-d H:i:s', $value); + $hasTime = true; + } + + // check that the creation could occurs. + if (false === $date) { + throw new Exception(sprintf('The value %s could ' + . 'not be converted to %s', $value, DateTime::class)); + } + + if (!$hasTime) { + $date->setTime(0, 0, 0); + } + + return $date; + }; + + case 'createdBy': + case 'updatedBy': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + return $this->userRepository->find($value)->getLabel(); + }; + + case 'civility': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $civility = $this->civilityRepository->find($value); + + if (null === $civility) { + return ''; + } + + return $this->translatableStringHelper->localize($civility->getName()); + }; + + case 'gender': + // for gender, we have to translate men/women statement + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $this->translator->trans($value); + }; + + case 'maritalStatus': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $maritalStatus = $this->maritalStatusRepository->find($value); + + return $this->translatableStringHelper->localize($maritalStatus->getName()); + }; + + case 'spokenLanguages': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $ids = json_decode($value); + + return + implode( + '|', + array_map(function ($id) { + if (null === $id) { + return ''; + } + + $lang = $this->languageRepository->find($id); + + if (null === $lang) { + return null; + } + + return $this->translatableStringHelper->localize($lang->getName()); + }, $ids) + ); + }; + + case 'countryOfBirth': + case 'nationality': + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + if (null === $value) { + return ''; + } + + $country = $this->countryRepository->find($value); + + return $this->translatableStringHelper->localize( + $country->getName() + ); + }; + + default: + // for fields which are associated with person + if (in_array($key, ListPersonHelper::FIELDS, true)) { + return function ($value) use ($key) { + if ('_header' === $value) { + return $this->translator->trans($key); + } + + return $value; + }; + } + } + } + + private function addCurrentAddressAt(QueryBuilder $qb, DateTimeImmutable $date): void + { + if (!(in_array('personHouseholdAddress', $qb->getAllAliases(), true))) { + $qb + ->leftJoin('person.householdAddresses', 'personHouseholdAddress') + ->andWhere( + $qb->expr()->orX( + // no address at this time + $qb->expr()->isNull('personHouseholdAddress'), + // there is one address... + $qb->expr()->andX( + $qb->expr()->lte('personHouseholdAddress.validFrom', ':address_date'), + $qb->expr()->orX( + $qb->expr()->isNull('personHouseholdAddress.validTo'), + $qb->expr()->gt('personHouseholdAddress.validTo', ':address_date') + ) + ) + ) + ) + ->setParameter('address_date', $date); + } + } +}