From 48772efd548a6fbcfbbba967608c2bccee6e53b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Tue, 28 Mar 2023 22:32:08 +0200 Subject: [PATCH] DX: fix phpstan issues --- phpstan.neon.dist | 2 +- .../Filter/ACPFilters/LocationFilter.php | 7 - .../src/Export/Export/ListAsideActivity.php | 6 +- .../Controller/ElementController.php | 12 - .../Repository/ChargeKindRepository.php | 8 +- .../ChargeKindRepositoryInterface.php | 12 +- .../Repository/ResourceKindRepository.php | 8 +- .../ResourceKindRepositoryInterface.php | 12 +- .../Controller/CalendarController.php | 15 - .../ChillCalendarBundle/Entity/Calendar.php | 5 +- .../Export/Export/CountCalendars.php | 2 +- .../Doctrine/Type/NativeDateIntervalType.php | 3 - .../Export/ExportInterface.php | 8 +- .../DataMapper/ExportPickCenterDataMapper.php | 24 +- .../EntityWorkflowHandlerInterface.php | 1 + .../Command/ImportPeopleFromCSVCommand.php | 1183 ----------------- .../Controller/HouseholdController.php | 2 +- .../Entity/AccompanyingPeriod.php | 18 +- .../Entity/Household/Household.php | 13 +- .../Form/Type/PickPersonDynamicType.php | 8 +- .../AccompanyingPeriodContext.php | 5 +- .../AccompanyingPeriodWorkWorkflowHandler.php | 4 +- .../Export/Export/ReportList.php | 2 +- .../DataFixtures/ORM/LoadThirdParty.php | 16 - .../Entity/ThirdParty.php | 5 +- .../src/Controller/Convert.php | 2 +- tests/app | 2 +- 27 files changed, 76 insertions(+), 1309 deletions(-) delete mode 100644 src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 611f6d71e..56b7c2228 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,7 +3,7 @@ parameters: paths: - src/ tmpDir: .cache/ - reportUnmatchedIgnoredErrors: true + reportUnmatchedIgnoredErrors: false excludePaths: - .php_cs* - docs/ diff --git a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php index 5cdce5b31..3d69d1633 100644 --- a/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php +++ b/src/Bundle/ChillActivityBundle/Export/Filter/ACPFilters/LocationFilter.php @@ -20,13 +20,6 @@ use Symfony\Component\Form\FormBuilderInterface; class LocationFilter implements FilterInterface { - private TranslatableStringHelper $translatableStringHelper; - - public function __construct(TranslatableStringHelper $translatableStringHelper) - { - $this->translatableStringHelper = $translatableStringHelper; - } - public function addRole(): ?string { return null; diff --git a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php index 976f4171c..aee168174 100644 --- a/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php +++ b/src/Bundle/ChillAsideActivityBundle/src/Export/Export/ListAsideActivity.php @@ -160,8 +160,8 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface return 'export.aside_activity.main_center'; } - /** @var Center $c */ if (null === $value || '' === $value || null === $c = $this->centerRepository->find($value)) { + /** @var Center $c */ return ''; } @@ -190,10 +190,6 @@ final class ListAsideActivity implements ListInterface, GroupedExportInterface ]; } - /** - * @param QueryBuilder $query - * @param array $data - */ public function getResult($query, $data): array { return $query->getQuery()->getResult(AbstractQuery::HYDRATE_ARRAY); diff --git a/src/Bundle/ChillBudgetBundle/Controller/ElementController.php b/src/Bundle/ChillBudgetBundle/Controller/ElementController.php index 70730e5c9..b5db3cd30 100644 --- a/src/Bundle/ChillBudgetBundle/Controller/ElementController.php +++ b/src/Bundle/ChillBudgetBundle/Controller/ElementController.php @@ -33,27 +33,15 @@ class ElementController extends AbstractController { private CalculatorManager $calculator; - private LoggerInterface $chillMainLogger; - - private EntityManagerInterface $em; - private ResourceRepository $resourceRepository; private ChargeRepository $chargeRepository; - private TranslatorInterface $translator; - public function __construct( - EntityManagerInterface $em, - TranslatorInterface $translator, - LoggerInterface $chillMainLogger, CalculatorManager $calculator, ResourceRepository $resourceRepository, ChargeRepository $chargeRepository, ) { - $this->em = $em; - $this->translator = $translator; - $this->chillMainLogger = $chillMainLogger; $this->calculator = $calculator; $this->resourceRepository = $resourceRepository; $this->chargeRepository = $chargeRepository; diff --git a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php index 01f5cd737..9aaf68ddf 100644 --- a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php +++ b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepository.php @@ -30,7 +30,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface } /** - * @return ChargeType[] + * @return array */ public function findAll(): array { @@ -38,7 +38,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface } /** - * @return ChargeType[] + * @return array */ public function findAllActive(): array { @@ -53,7 +53,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface } /** - * @return ChargeType[] + * @return array */ public function findAllByType(string $type): array { @@ -64,7 +64,7 @@ final class ChargeKindRepository implements ChargeKindRepositoryInterface * @param mixed|null $limit * @param mixed|null $offset * - * @return ChargeType[] + * @return array */ public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array { diff --git a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php index fb8c9dc35..f92851eba 100644 --- a/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php +++ b/src/Bundle/ChillBudgetBundle/Repository/ChargeKindRepositoryInterface.php @@ -19,25 +19,25 @@ interface ChargeKindRepositoryInterface extends ObjectRepository public function find($id): ?ChargeKind; /** - * @return ChargeType[] + * @return array */ public function findAll(): array; /** - * @return ChargeType[] + * @return array */ public function findAllActive(): array; /** - * @return ChargeType[] + * @return array */ public function findAllByType(string $type): array; /** - * @param mixed|null $limit - * @param mixed|null $offset + * @param int|null $limit + * @param int|null $offset * - * @return ChargeType[] + * @return array */ public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array; diff --git a/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php b/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php index 42e5418fc..1f9f99753 100644 --- a/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php +++ b/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepository.php @@ -30,7 +30,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface } /** - * @return ResourceType[] + * @return list */ public function findAll(): array { @@ -38,7 +38,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface } /** - * @return ResourceType[] + * @return list */ public function findAllActive(): array { @@ -58,7 +58,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface } /** - * @return ResourceType[] + * @return list */ public function findAllByType(string $type): array { @@ -69,7 +69,7 @@ final class ResourceKindRepository implements ResourceKindRepositoryInterface * @param mixed|null $limit * @param mixed|null $offset * - * @return ResourceType[] + * @return list */ public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array { diff --git a/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepositoryInterface.php b/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepositoryInterface.php index 658a87a3d..dac46ff35 100644 --- a/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepositoryInterface.php +++ b/src/Bundle/ChillBudgetBundle/Repository/ResourceKindRepositoryInterface.php @@ -19,25 +19,25 @@ interface ResourceKindRepositoryInterface extends ObjectRepository public function find($id): ?ResourceKind; /** - * @return ResourceType[] + * @return list */ public function findAll(): array; /** - * @return ResourceType[] + * @return list */ public function findAllActive(): array; /** - * @return ResourceType[] + * @return list */ public function findAllByType(string $type): array; /** - * @param mixed|null $limit - * @param mixed|null $offset + * @param int|null $limit + * @param int|null $offset * - * @return ResourceType[] + * @return list */ public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array; diff --git a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php index 550c6e984..47883702f 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/CalendarController.php @@ -544,21 +544,6 @@ class CalendarController extends AbstractController return $filterOrder->build(); } - private function buildParamsToUrl(?User $user, ?AccompanyingPeriod $accompanyingPeriod): array - { - $params = []; - - if (null !== $user) { - $params['user_id'] = $user->getId(); - } - - if (null !== $accompanyingPeriod) { - $params['id'] = $accompanyingPeriod->getId(); - } - - return $params; - } - /** * Creates a form to delete a Calendar entity by id. */ diff --git a/src/Bundle/ChillCalendarBundle/Entity/Calendar.php b/src/Bundle/ChillCalendarBundle/Entity/Calendar.php index 56373ca17..66e6a6d69 100644 --- a/src/Bundle/ChillCalendarBundle/Entity/Calendar.php +++ b/src/Bundle/ChillCalendarBundle/Entity/Calendar.php @@ -29,6 +29,7 @@ use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ReadableCollection; use Doctrine\ORM\Mapping as ORM; use LogicException; use Symfony\Component\Serializer\Annotation as Serializer; @@ -507,10 +508,10 @@ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCente } /** - * @return Collection|User[] + * @return ReadableCollection<(int|string), User> * @Serializer\Groups({"calendar:read", "read"}) */ - public function getUsers(): Collection + public function getUsers(): ReadableCollection { return $this->getInvites()->map(static function (Invite $i) { return $i->getUser(); diff --git a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php index 06d6defca..f3bf79547 100644 --- a/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php +++ b/src/Bundle/ChillCalendarBundle/Export/Export/CountCalendars.php @@ -52,7 +52,7 @@ class CountCalendars implements ExportInterface, GroupedExportInterface return 'Exports of calendar'; } - public function getLabels($key, array $values, $data): Closure + public function getLabels($key, array $values, $data) { if ('export_result' !== $key) { throw new LogicException("the key {$key} is not used by this export"); diff --git a/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php b/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php index 1d4cd104c..896ddb600 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php +++ b/src/Bundle/ChillMainBundle/Doctrine/Type/NativeDateIntervalType.php @@ -31,9 +31,6 @@ class NativeDateIntervalType extends DateIntervalType { public const FORMAT = '%rP%YY%MM%DDT%HH%IM%SS'; - /** - * @param DateInterval|null $value - */ public function convertToDatabaseValue($value, AbstractPlatform $platform) { if (null === $value) { diff --git a/src/Bundle/ChillMainBundle/Export/ExportInterface.php b/src/Bundle/ChillMainBundle/Export/ExportInterface.php index 95122ec0a..d4e456ca6 100644 --- a/src/Bundle/ChillMainBundle/Export/ExportInterface.php +++ b/src/Bundle/ChillMainBundle/Export/ExportInterface.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Export; +use Doctrine\ORM\NativeQuery; use Doctrine\ORM\QueryBuilder; /** @@ -23,6 +24,7 @@ use Doctrine\ORM\QueryBuilder; * aggregation, use `ListInterface`. * * @example Chill\PersonBundle\Export\CountPerson an example of implementation + * @template Q of QueryBuilder|NativeQuery */ interface ExportInterface extends ExportElementInterface { @@ -84,7 +86,7 @@ interface ExportInterface extends ExportElementInterface * @param mixed[] $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR') * @param mixed $data The data from the export's form (as defined in `buildForm`) * - * @return pure-callable(null|string|int|float|'_header' $value):string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }` + * @return callable(null|string|int|float|'_header' $value): string|int|\DateTimeInterface where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }` */ public function getLabels($key, array $values, $data); @@ -102,7 +104,7 @@ interface ExportInterface extends ExportElementInterface /** * Return the results of the query builder. * - * @param \Doctrine\ORM\NativeQuery|QueryBuilder $query + * @param Q $query * @param mixed[] $data the data from the export's fomr (added by self::buildForm) * * @return mixed[] an array of results @@ -132,7 +134,7 @@ interface ExportInterface extends ExportElementInterface * @param array $acl an array where each row has a `center` key containing the Chill\MainBundle\Entity\Center, and `circles` keys containing the reachable circles. Example: `array( array('center' => $centerA, 'circles' => array($circleA, $circleB) ) )` * @param array $data the data from the form, if any * - * @return \Doctrine\ORM\NativeQuery|QueryBuilder the query to execute. + * @return Q the query to execute. */ public function initiateQuery(array $requiredModifiers, array $acl, array $data = []); diff --git a/src/Bundle/ChillMainBundle/Form/DataMapper/ExportPickCenterDataMapper.php b/src/Bundle/ChillMainBundle/Form/DataMapper/ExportPickCenterDataMapper.php index 549805fc5..52e053795 100644 --- a/src/Bundle/ChillMainBundle/Form/DataMapper/ExportPickCenterDataMapper.php +++ b/src/Bundle/ChillMainBundle/Form/DataMapper/ExportPickCenterDataMapper.php @@ -24,15 +24,7 @@ class ExportPickCenterDataMapper implements DataMapperInterface { protected RegroupmentRepository $regroupmentRepository; - /** - * @param array|Center[] $data - * @param $forms - * - * @throws Exception - * - * @return void - */ - public function mapDataToForms($data, $forms) + public function mapDataToForms($data, $forms): void { if (null === $data) { return; @@ -44,7 +36,9 @@ class ExportPickCenterDataMapper implements DataMapperInterface $pickedRegroupment = []; foreach ($this->regroupmentRepository->findAll() as $regroupment) { - [$contained, $notContained] = $regroupment->getCenters()->partition(static function (Center $center) { + /** @phpstan-ignore-next-line */ + [$contained, $notContained] = $regroupment->getCenters()->partition(static function (Center $center): bool { + return false; }); if (0 === count($notContained)) { @@ -56,13 +50,7 @@ class ExportPickCenterDataMapper implements DataMapperInterface $form['centers']->setData($data); } - /** - * @param iterable $forms - * @param array $data - * - * @return void - */ - public function mapFormsToData($forms, &$data) + public function mapFormsToData($forms, &$data): void { /** @var array $forms */ $forms = iterator_to_array($forms); @@ -74,8 +62,8 @@ class ExportPickCenterDataMapper implements DataMapperInterface } if (array_key_exists('regroupment', $forms)) { + /** @var Regroupment $regroupment */ foreach ($forms['regroupment']->getData() as $regroupment) { - /** @var Regroupment $regroupment */ foreach ($regroupment->getCenters() as $center) { $centers[spl_object_hash($center)] = $center; } diff --git a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php index 73e260c37..1d185ba0e 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php +++ b/src/Bundle/ChillMainBundle/Workflow/EntityWorkflowHandlerInterface.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\MainBundle\Workflow; +use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; interface EntityWorkflowHandlerInterface diff --git a/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php b/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php deleted file mode 100644 index 139305a0d..000000000 --- a/src/Bundle/ChillPersonBundle/Command/ImportPeopleFromCSVCommand.php +++ /dev/null @@ -1,1183 +0,0 @@ -logger = $logger; - $this->helper = $helper; - $this->em = $em; - $this->customFieldProvider = $customFieldProvider; - $this->eventDispatcher = $eventDispatcher; - $this->formFactory = $formFactory; - - parent::__construct('chill:person:import'); - } - - /** - * This function is a shortcut to addOption. - * - * @param string $name - * @param string $description - * @param string $default - * - * @return ImportPeopleFromCSVCommand - */ - protected function addOptionShortcut($name, $description, $default) - { - $this->addOption($name, null, InputOption::VALUE_OPTIONAL, $description, $default); - - return $this; - } - - protected function configure() - { - $this - ->addArgument('csv_file', InputArgument::REQUIRED, 'The CSV file to import') - ->setDescription('Import people from a csv file') - ->setHelp( - <<<'EOF' - Import people from a csv file. The first row must contains the header column and will determines where the value will be matched. - - Date format: the possible date format may be separatedby an |. The possible format will be tryed from the first to the last. The format should be explained as http://php.net/manual/en/function.strftime.php - - php app/console chill:person:import /tmp/hepc.csv fr_FR.utf8 \ - --firstname="Prénom" --lastname="Nom" \ - --birthdate="D.N." --birthdate_format="%d/%m/%Y" \ - --opening_date_format="%B %Y|%Y" --closing_date="der.contact" \ - --closing_date_format="%Y" --custom-field="3=code" -vvv - EOF - ) - ->addArgument( - 'locale', - InputArgument::REQUIRED, - 'The locale to use in displaying translatable strings from entities' - ) - ->addOption( - 'force-center', - null, - InputOption::VALUE_REQUIRED, - 'The id of the center' - ) - ->addOption( - 'force', - null, - InputOption::VALUE_NONE, - 'Persist people in the database (default is not to persist people)' - ) - ->addOption( - 'delimiter', - 'd', - InputOption::VALUE_OPTIONAL, - 'The delimiter character of the csv file', - ',' - ) - ->addOption( - 'enclosure', - null, - InputOption::VALUE_OPTIONAL, - 'The enclosure character of the csv file', - '"' - ) - ->addOption( - 'escape', - null, - InputOption::VALUE_OPTIONAL, - 'The escape character of the csv file', - '\\' - ) - ->addOption( - 'length', - null, - InputOption::VALUE_OPTIONAL, - 'The length of line to read. 0 means unlimited.', - 0 - ) - ->addOption( - 'dump-choice-matching', - null, - InputOption::VALUE_REQUIRED, - 'The path of the file to dump the matching between label in CSV and answers' - ) - ->addOption( - 'load-choice-matching', - null, - InputOption::VALUE_OPTIONAL, - 'The path of the file to load the matching between label in CSV and answers' - ); - - // mapping columns - foreach (self::$mapping as $m) { - $this->addOptionShortcut($m[0], $m[1], $m[2]); - } - - // other information - $this->addOptionShortcut( - 'birthdate_format', - 'Format preference for ' - . 'birthdate. See help for date formats preferences.', - self::$defaultDateInterpreter - ); - $this->addOptionShortcut( - 'opening_date_format', - 'Format preference for ' - . 'opening date. See help for date formats preferences.', - self::$defaultDateInterpreter - ); - $this->addOptionShortcut( - 'closing_date_format', - 'Format preference for ' - . 'closing date. See help for date formats preferences.', - self::$defaultDateInterpreter - ); - - // mapping column to custom fields - $this->addOption( - 'custom-field', - null, - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Mapping a column to a custom fields key. Example: 1=cf_slug' - ); - $this->addOption( - 'skip-interactive-field-mapping', - null, - InputOption::VALUE_NONE, - 'Do not ask for interactive mapping' - ); - } - - /** - * @param array $headers the processed header : an array as prepared by self::processingHeaders - * - * @throws Exception - */ - protected function createPerson(array $row, array $headers): Person - { - // trying to get the opening date - $openingDateString = trim($row[array_search('opening_date', $headers, true)]); - $openingDate = $this->processDate($openingDateString, $this->input->getOption('opening_date_format')); - - $person = new Person(); - // add the center - $center = $this->getCenter($row, $headers); - - if (null === $center) { - throw new Exception('center not found'); - } - - $person->setCenter($center); - - foreach ($headers as $column => $info) { - $value = trim($row[$column]); - - switch ($info) { - case 'firstname': - $person->setFirstName($value); - - break; - - case 'lastname': - $person->setLastName($value); - - break; - - case 'birthdate': - $this->processBirthdate($person, $value); - - break; - - case 'gender': - $person->setGender($value); - - break; - - case 'opening_date': - // we have processed this when creating the person object, skipping; - break; - - case 'closing_date': - $this->processClosingDate($person, $value); - - break; - - case 'memo': - $person->setMemo($value); - - break; - - case 'email': - $person->setEmail($value); - - break; - - case 'phonenumber': - $person->setPhonenumber($value); - - break; - - case 'mobilenumber': - $person->setMobilenumber($value); - - break; - // we just keep the column number for those data - case 'postalcode': - $postalCodeValue = $value; - - break; - - case 'street1': - $street1Value = $value; - - break; - - case 'locality': - $localityValue = $value; - - break; - } - } - - // handle address - if (in_array('postalcode', $headers, true)) { - if (!empty($postalCodeValue)) { - $address = new Address(); - $postalCode = $this->guessPostalCode($postalCodeValue, $localityValue ?? ''); - - if (null === $postalCode) { - throw new Exception('The locality is not found'); - } - - $address->setPostcode($postalCode); - - if (in_array('street1', $headers, true)) { - /** @phpstan-ignore-next-line as this command is deprecated */ - $address->setStreetAddress1($street1Value); - } - $address->setValidFrom(new DateTime('today')); - - $person->addAddress($address); - } - } - - return $person; - } - - protected function dumpAnswerMatching() - { - if ($this->input->hasOption('dump-choice-matching') && !empty($this->input->getOption('dump-choice-matching'))) { - $this->logger->debug('Dump the matching between answer and choices'); - $str = json_encode($this->cacheAnswersMapping, JSON_PRETTY_PRINT); - - $fs = new Filesystem(); - $filename = $this->input->getOption('dump-choice-matching'); - - $fs->dumpFile($filename, $str); - } - } - - /** - * @throws Exception - * - * @return int|void|null - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $headers = $rawHeaders = []; - $this->input = $input; - $this->output = $output; - - $this->logger->debug('Setting locale to ' . $input->getArgument('locale')); - setlocale(LC_TIME, $input->getArgument('locale')); - - // opening csv as resource - $csv = $this->openCSV(); - - $num = 0; - $line = $this->line = 1; - - try { - while ( - false !== ($row = fgetcsv( - $csv, - $input->getOption('length'), - $input->getOption('delimiter'), - $input->getOption('enclosure'), - $input->getOption('escape') - )) - ) { - $this->logger->debug('Processing line ' . $this->line); - - if (1 === $line) { - $this->logger->debug('Processing line 1, headers'); - $rawHeaders = $row; - $headers = $this->processingHeaders($row); - } else { - $person = $this->createPerson($row, $headers); - - if (count($this->customFieldMapping) > 0) { - $this->processingCustomFields($person, $row); - } - - $event = new Event(); - $event->person = $person; - $event->rawHeaders = $rawHeaders; - $event->row = $row; - $event->headers = $headers; - $event->skipPerson = false; - $event->force = $this->input->getOption('force'); - $event->input = $this->input; - $event->output = $this->output; - $event->helperSet = $this->getHelperSet(); - - $this->eventDispatcher->dispatch('chill_person.person_import', $event); - - if ( - $this->input->getOption('force') === true - && false === $event->skipPerson - ) { - $this->em->persist($person); - } - - ++$num; - } - - ++$line; - ++$this->line; - } - - if ($this->input->getOption('force') === true) { - $this->logger->debug('persisting entitites'); - $this->em->flush(); - } - } finally { - $this->logger->debug('closing csv', ['method' => __METHOD__]); - fclose($csv); - // dump the matching between answer and choices - $this->dumpAnswerMatching(); - } - } - - /** - * @return Center|mixed|object|null - */ - protected function getCenter(array $row, array $headers) - { - if ($this->input->hasOption('force-center') && !empty($this->input->getOption('force-center'))) { - return $this->em->getRepository(Center::class)->find($this->input->getOption('force-center')); - } - - $columnCenter = array_search('center', $headers, true); - $centerName = trim($row[$columnCenter]); - - try { - return $this - ->em - ->createQuery('SELECT c FROM ChillMainBundle:Center c WHERE c.name = :center_name') - ->setParameter('center_name', $centerName) - ->getSingleResult(); - } catch (NonUniqueResultException $e) { - return $this->guessCenter($centerName); - } catch (NoResultException $e) { - return $this->guessCenter($centerName); - } - } - - /** - * @param $centerName - * - * @return Center|mixed|object|null - */ - protected function guessCenter($centerName) - { - if (!array_key_exists('_center_picked', $this->cacheAnswersMapping)) { - $this->cacheAnswersMapping['_center_picked'] = []; - } - - if (array_key_exists($centerName, $this->cacheAnswersMapping['_center_picked'])) { - $id = $this->cacheAnswersMapping['_center_picked'][$centerName]; - - return $this->em->getRepository(Center::class) - ->find($id); - } - - $centers = $this->em->createQuery('SELECT c FROM ChillMainBundle:Center c ' - . 'ORDER BY SIMILARITY(c.name, :center_name) DESC') - ->setParameter('center_name', $centerName) - ->setMaxResults(10) - ->getResult(); - - if (count($centers) > 1) { - if (strtolower($centers[0]->getName()) === strtolower($centerName)) { - return $centers[0]; - } - } - - $centersByName = []; - $names = array_map(static function (Center $c) use (&$centersByName) { - $n = $c->getName(); - $centersByName[$n] = $c; - - return $n; - }, $centers); - $names[] = 'none of them'; - - $helper = $this->getHelper('question'); - $question = new ChoiceQuestion( - sprintf('Which center match the name "%s" ? (default to "%s")', $centerName, $names[0]), - $names, - 0 - ); - - $answer = $helper->ask($this->input, $this->output, $question); - - if ('none of them' === $answer) { - $questionCreate = new ConfirmationQuestion('Would you like to create it ?', false); - $create = $helper->ask($this->input, $this->output, $questionCreate); - - if ($create) { - $center = (new Center()) - ->setName($centerName); - - if ($this->input->getOption('force') === true) { - $this->em->persist($center); - $this->em->flush(); - } - - return $center; - } - } - - $center = $centersByName[$answer]; - - $this->cacheAnswersMapping['_center_picked'][$centerName] = $center->getId(); - - return $center; - } - - /** - * @param $postalCode - * @param $locality - * - * @return mixed|null - */ - protected function guessPostalCode($postalCode, $locality) - { - if (!array_key_exists('_postal_code_picked', $this->cacheAnswersMapping)) { - $this->cacheAnswersMapping['_postal_code_picked'] = []; - } - - if (array_key_exists($postalCode, $this->cacheAnswersMapping['_postal_code_picked'])) { - if (array_key_exists($locality, $this->cacheAnswersMapping['_postal_code_picked'][$postalCode])) { - $id = $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality]; - - return $this->em->getRepository(PostalCode::class)->find($id); - } - } - - $postalCodes = $this->em->createQuery( - 'SELECT pc FROM ' . PostalCode::class . ' pc ' - . 'WHERE pc.code = :postal_code ' - . 'ORDER BY SIMILARITY(pc.name, :locality) DESC ' - ) - ->setMaxResults(10) - ->setParameter('postal_code', $postalCode) - ->setParameter('locality', $locality) - ->getResult(); - - if (count($postalCodes) >= 1) { - if ( - $postalCodes[0]->getCode() === $postalCode - && $postalCodes[0]->getName() === $locality - ) { - return $postalCodes[0]; - } - } - - if (count($postalCodes) === 0) { - return null; - } - - $postalCodeByName = []; - $names = array_map(static function (PostalCode $pc) use (&$postalCodeByName) { - $n = $pc->getName(); - $postalCodeByName[$n] = $pc; - - return $n; - }, $postalCodes); - $names[] = 'none of them'; - - $helper = $this->getHelper('question'); - $question = new ChoiceQuestion( - sprintf( - 'Which postal code match the ' - . 'name "%s" with postal code "%s" ? (default to "%s")', - $locality, - $postalCode, - $names[0] - ), - $names, - 0 - ); - - $answer = $helper->ask($this->input, $this->output, $question); - - if ('none of them' === $answer) { - return null; - } - - $pc = $postalCodeByName[$answer]; - - $this->cacheAnswersMapping['_postal_code_picked'][$postalCode][$locality] = - $pc->getId(); - - return $pc; - } - - protected function interact(InputInterface $input, OutputInterface $output) - { - // preparing the basic - $this->input = $input; - $this->output = $output; - $this->logger = new ConsoleLogger($output); - - $csv = $this->openCSV(); - - // getting the first row - if ( - false !== ($row = fgetcsv( - $csv, - $input->getOption('length'), - $input->getOption('delimiter'), - $input->getOption('enclosure'), - $input->getOption('escape') - )) - ) { - try { - $this->matchColumnToCustomField($row); - } finally { - $this->logger->debug('closing csv', ['method' => __METHOD__]); - fclose($csv); - } - } - - // load the matching between csv and label - $this->loadAnswerMatching(); - } - - /** - * Load the mapping between answer in CSV and value in choices from a json file. - */ - protected function loadAnswerMatching() - { - if ($this->input->hasOption('load-choice-matching')) { - $fs = new Filesystem(); - $filename = $this->input->getOption('load-choice-matching'); - - if (!$fs->exists($filename)) { - $this->logger->warning("The file {$filename} is not found. Choice matching not loaded"); - } else { - $this->logger->debug("Loading {$filename} as choice matching"); - $this->cacheAnswersMapping = json_decode(file_get_contents($filename), true); - } - } - } - - /** - * @param $row - */ - protected function matchColumnToCustomField($row) - { - $cfMappingsOptions = $this->input->getOption('custom-field'); - /** @var \Doctrine\Persistence\ObjectManager $em */ - $em = $this->em; - - foreach ($cfMappingsOptions as $cfMappingStringOption) { - [$rowNumber, $cfSlug] = preg_split('|=|', $cfMappingStringOption); - - // check that the column exists, getting the column name - $column = $row[$rowNumber]; - - if (empty($column)) { - $message = "The column with row {$rowNumber} is empty."; - $this->logger->error($message); - - throw new RuntimeException($message); - } - - // check a custom field exists - try { - $customField = $em->createQuery('SELECT cf ' - . 'FROM ChillCustomFieldsBundle:CustomField cf ' - . 'JOIN cf.customFieldGroup g ' - . 'WHERE cf.slug = :slug ' - . 'AND g.entity = :entity') - ->setParameters([ - 'slug' => $cfSlug, - 'entity' => Person::class, - ]) - ->getSingleResult(); - } catch (\Doctrine\ORM\NoResultException $e) { - $message = sprintf( - "The customfield with slug '%s' does not exists. It was associated with column number %d", - $cfSlug, - $rowNumber - ); - $this->logger->error($message); - - throw new RuntimeException($message); - } - // skip if custom field does not exists - if (null === $customField) { - $this->logger->error("The custom field with slug {$cfSlug} could not be found. " - . 'Stopping this command.'); - - throw new RuntimeException("The custom field with slug {$cfSlug} could not be found. " - . 'Stopping this command.'); - } - - $this->logger->notice(sprintf( - "Matched custom field %s (question : '%s') on column %d (displayed in the file as '%s')", - $customField->getSlug(), - $this->helper->localize($customField->getName()), - $rowNumber, - $column - )); - - $this->customFieldMapping[$rowNumber] = $customField; - } - } - - /** - * @throws RuntimeException - * - * @return resource - */ - protected function openCSV() - { - $fs = new Filesystem(); - $filename = $this->input->getArgument('csv_file'); - - if (!$fs->exists($filename)) { - throw new RuntimeException('The file does not exists or you do not ' - . 'have the right to read it.'); - } - - $resource = fopen($filename, 'rb'); - - if (false === $resource) { - throw new RuntimeException("The file '{$filename}' could not be opened."); - } - - return $resource; - } - - /** - * @param $value - * - * @throws Exception - */ - protected function processBirthdate(Person $person, $value) - { - if (empty($value)) { - return; - } - - $date = $this->processDate($value, $this->input->getOption('birthdate_format')); - - if ($date instanceof DateTime) { - // we correct birthdate if the date is in the future - // the most common error is to set date 100 years to late (ex. 2063 instead of 1963) - if ($date > new DateTime('yesterday')) { - $date = $date->sub(new DateInterval('P100Y')); - } - - $person->setBirthdate($date); - - return; - } - - // if we arrive here, we could not process the date - $this->logger->warning(sprintf( - 'Line %d : the birthdate could not be interpreted. Was %s.', - $this->line, - $value - )); - } - - /** - * Process a custom field choice. - * - * The method try to guess if the result exists amongst the text of the possible - * choices. If the texts exists, then this is picked. Else, ask the user. - * - * @param string $value - * - * @throws Exception - * - * @return string - */ - protected function processChoiceType( - $value, - \Symfony\Component\Form\FormInterface $form, - \Chill\CustomFieldsBundle\Entity\CustomField $cf - ) { - // getting the possible answer and their value : - $view = $form->get($cf->getSlug())->createView(); - $answers = $this->collectChoicesAnswers($view->vars['choices']); - - // if we do not have any answer on the question, throw an error. - if (count($answers) === 0) { - $message = sprintf( - "The question '%s' with slug '%s' does not count any answer.", - $this->helper->localize($cf->getName()), - $cf->getSlug() - ); - - $this->logger->error($message, [ - 'method' => __METHOD__, - 'slug' => $cf->getSlug(), - 'question' => $this->helper->localize($cf->getName()), - ]); - - throw new RuntimeException($message); - } - - if (false === $view->vars['required']) { - $answers[null] = '** no answer'; - } - - // the answer does not exists in cache. Try to find it, or asks the user - if (!isset($this->cacheAnswersMapping[$cf->getSlug()][$value])) { - // try to find the answer (with array_keys and a search value - $values = array_keys( - array_map(static function ($label) { - return trim(strtolower($label)); - }, $answers), - trim(strtolower($value)), - true - ); - - if (count($values) === 1) { - // we could guess an answer ! - $this->logger->info('This question accept multiple answers'); - $this->cacheAnswersMapping[$cf->getSlug()][$value] = - false === $view->vars['multiple'] ? $values[0] : [$values[0]]; - $this->logger->info(sprintf( - "Guessed that value '%s' match with key '%s' " - . 'because the CSV and the label are equals.', - $value, - $values[0] - )); - } else { - // we could nog guess an answer. Asking the user. - $this->output->writeln('I do not know the answer to this question : '); - $this->output->writeln($this->helper->localize($cf->getName())); - - // printing the possible answers - /** @var \Symfony\Component\Console\Helper\Table $table */ - $table = new Table($this->output); - $table->setHeaders(['#', 'label', 'value']); - $i = 0; - - $matchingTableRowAnswer = []; - - foreach ($answers as $key => $answer) { - $table->addRow([ - $i, $answer, $key, - ]); - $matchingTableRowAnswer[$i] = $key; - ++$i; - } - $table->render($this->output); - - $question = new ChoiceQuestion( - sprintf('Please pick your choice for the value "%s"', $value), - array_keys($matchingTableRowAnswer) - ); - $question->setErrorMessage('This choice is not possible'); - - if ($view->vars['multiple']) { - $this->logger->debug('this question is multiple'); - $question->setMultiselect(true); - } - - $selected = $this->getHelper('question')->ask($this->input, $this->output, $question); - - $this->output->writeln( - sprintf( - 'You have selected "%s"', - is_array($answers[$matchingTableRowAnswer[$selected]]) ? - implode(',', $answers[$matchingTableRowAnswer[$selected]]) : - $answers[$matchingTableRowAnswer[$selected]] - ) - ); - - // recording value in cache - $this->cacheAnswersMapping[$cf->getSlug()][$value] = $matchingTableRowAnswer[$selected]; - $this->logger->debug(sprintf( - "Setting the value '%s' in cache for customfield '%s' and answer '%s'", - is_array($this->cacheAnswersMapping[$cf->getSlug()][$value]) ? - implode(', ', $this->cacheAnswersMapping[$cf->getSlug()][$value]) : - $this->cacheAnswersMapping[$cf->getSlug()][$value], - $cf->getSlug(), - $value - )); - } - } - - $form->submit([$cf->getSlug() => $this->cacheAnswersMapping[$cf->getSlug()][$value]]); - $value = $form->getData()[$cf->getSlug()]; - - $this->logger->debug( - sprintf( - "Found value : %s for custom field with question '%s'", - is_array($value) ? implode(',', $value) : $value, - $this->helper->localize($cf->getName()) - ) - ); - - return $value; - } - - /** - * @param $value - * - * @throws Exception - */ - protected function processClosingDate(Person $person, $value) - { - if (empty($value)) { - return; - } - - // we skip if the opening date is now (or after yesterday) - /** @var \Chill\PersonBundle\Entity\AccompanyingPeriod $period */ - $period = $person->getCurrentAccompanyingPeriod(); - - if ($period->getOpeningDate() > new DateTime('yesterday')) { - $this->logger->debug(sprintf( - 'skipping a closing date because opening date is after yesterday (%s)', - $period->getOpeningDate()->format('Y-m-d') - )); - - return; - } - - $date = $this->processDate($value, $this->input->getOption('closing_date_format')); - - if ($date instanceof DateTime) { - // we correct birthdate if the date is in the future - // the most common error is to set date 100 years to late (ex. 2063 instead of 1963) - if ($date > new DateTime('yesterday')) { - $date = $date->sub(new DateInterval('P100Y')); - } - - $period->setClosingDate($date); - $person->close(); - - return; - } - - // if we arrive here, we could not process the date - $this->logger->warning(sprintf( - 'Line %d : the closing date could not be interpreted. Was %s.', - $this->line, - $value - )); - } - - /** - * @param $value - * @param $formats - * - * @return bool|DateTime - */ - protected function processDate($value, $formats) - { - $possibleFormats = explode('|', $formats); - - foreach ($possibleFormats as $format) { - $this->logger->debug("Trying format {$format}", [__METHOD__]); - $dateR = strptime($value, $format); - - if (is_array($dateR) && '' === $dateR['unparsed']) { - $string = sprintf( - '%04d-%02d-%02d %02d:%02d:%02d', - ($dateR['tm_year'] + 1900), - ($dateR['tm_mon'] + 1), - ($dateR['tm_mday']), - ($dateR['tm_hour']), - ($dateR['tm_min']), - ($dateR['tm_sec']) - ); - $date = DateTime::createFromFormat('Y-m-d H:i:s', $string); - $this->logger->debug(sprintf('Interpreting %s as date %s', $value, $date->format('Y-m-d H:i:s'))); - - return $date; - } - } - - // if we arrive here, we could not process the date - $this->logger->debug(sprintf( - 'Line %d : a date could not be interpreted. Was %s.', - $this->line, - $value - )); - - return false; - } - - /** - * @param $row - * - * @throws Exception - */ - protected function processingCustomFields(Person $person, $row) - { - /** @var \Chill\CustomFieldsBundle\Service\CustomFieldProvider $cfProvider */ - $cfProvider = $this->customFieldProvider; - $cfData = []; - - /** @var \Chill\CustomFieldsBundle\Entity\CustomField $$customField */ - foreach ($this->customFieldMapping as $rowNumber => $customField) { - $builder = $this->formFactory->createBuilder(); - $cfProvider->getCustomFieldByType($customField->getType()) - ->buildForm($builder, $customField); - $form = $builder->getForm(); - - // get the type of the form - $type = get_class($form->get($customField->getSlug()) - ->getConfig()->getType()->getInnerType()); - $this->logger->debug(sprintf( - 'Processing a form of type %s', - $type - )); - - switch ($type) { - case \Symfony\Component\Form\Extension\Core\Type\TextType::class: - $cfData[$customField->getSlug()] = - $this->processTextType($row[$rowNumber], $form, $customField); - - break; - - case \Symfony\Component\Form\Extension\Core\Type\ChoiceType::class: - case \Chill\MainBundle\Form\Type\Select2ChoiceType::class: - $cfData[$customField->getSlug()] = - $this->processChoiceType($row[$rowNumber], $form, $customField); - } - } - - $person->setCFData($cfData); - } - - /** - * @return array where keys are column number, and value is information mapped - */ - protected function processingHeaders(array $firstRow): array - { - $availableOptions = array_map( - static fn (array $m) => $m[0], - self::$mapping - ); - $matchedColumnHeaders = $headers = []; - - foreach ($availableOptions as $option) { - $matchedColumnHeaders[$option] = $this->input->getOption($option); - } - - foreach ($firstRow as $key => $content) { - $content = trim($content); - - if (in_array($content, $matchedColumnHeaders, true)) { - $information = array_search($content, $matchedColumnHeaders, true); - $headers[$key] = $information; - $this->logger->notice("Matched {$information} on column {$key} (displayed in the file as '{$content}')"); - } else { - $this->logger->notice("Column with content '{$content}' is ignored"); - } - } - - return $headers; - } - - /** - * Process a text type on a custom field. - * - * @param type $value - * - * @return type - */ - protected function processTextType( - $value, - \Symfony\Component\Form\FormInterface $form, - \Chill\CustomFieldsBundle\Entity\CustomField $cf - ) { - $form->submit([$cf->getSlug() => $value]); - - $value = $form->getData()[$cf->getSlug()]; - - $this->logger->debug(sprintf('Found value : %s for custom field with question ' - . "'%s'", $value, $this->helper->localize($cf->getName()))); - - return $value; - } - - /** - * Recursive method to collect the possibles answer from a ChoiceType (or - * its inherited types). - * - * @param mixed $choices - * - * @throws Exception - * - * @return array where - */ - private function collectChoicesAnswers($choices) - { - $answers = []; - - /** @var \Symfony\Component\Form\ChoiceList\View\ChoiceView $choice */ - foreach ($choices as $choice) { - if ($choice instanceof \Symfony\Component\Form\ChoiceList\View\ChoiceView) { - $answers[$choice->value] = $choice->label; - } elseif ($choice instanceof \Symfony\Component\Form\ChoiceList\View\ChoiceGroupView) { - $answers = $answers + $this->collectChoicesAnswers($choice->choices); - } else { - throw new Exception(sprintf( - "The choice type is not know. Expected '%s' or '%s', get '%s'", - \Symfony\Component\Form\ChoiceList\View\ChoiceView::class, - \Symfony\Component\Form\ChoiceList\View\ChoiceGroupView::class, - get_class($choice) - )); - } - } - - return $answers; - } -} diff --git a/src/Bundle/ChillPersonBundle/Controller/HouseholdController.php b/src/Bundle/ChillPersonBundle/Controller/HouseholdController.php index ba7c78f65..7b9eb0084 100644 --- a/src/Bundle/ChillPersonBundle/Controller/HouseholdController.php +++ b/src/Bundle/ChillPersonBundle/Controller/HouseholdController.php @@ -82,7 +82,7 @@ class HouseholdController extends AbstractController } usort($accompanyingPeriods, static function ($a, $b) { - return $b->getOpeningDate() > $a->getOpeningDate(); + return $b->getOpeningDate() <=> $a->getOpeningDate(); }); $oldMembers = $household->getNonCurrentMembers(); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 4dd297b9f..510e1c327 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -42,6 +42,7 @@ use DateTimeInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ReadableCollection; use Doctrine\ORM\Mapping as ORM; use Iterator; use LogicException; @@ -615,9 +616,9 @@ class AccompanyingPeriod implements /** * Get a list of person which have an adresse available for a valid location. * - * @return Collection|Person[] + * @return ReadableCollection<(int|string), Person> */ - public function getAvailablePersonLocation(): Collection + public function getAvailablePersonLocation(): ReadableCollection { return $this->getOpenParticipations() ->filter( @@ -675,8 +676,9 @@ class AccompanyingPeriod implements /** * @Groups({"read"}) + * @return ReadableCollection<(int|string), Comment> */ - public function getComments(): Collection + public function getComments(): ReadableCollection { $pinnedComment = $this->pinnedComment; @@ -700,7 +702,7 @@ class AccompanyingPeriod implements /** * @Groups({"docgen:read"}) */ - public function getCurrentParticipations(): Collection + public function getCurrentParticipations(): ReadableCollection { return $this->getOpenParticipations(); } @@ -834,7 +836,10 @@ class AccompanyingPeriod implements return $collection->count() > 0 ? $collection->first() : null; } - public function getOpenParticipations(): Collection + /** + * @return ReadableCollection<(int|string), AccompanyingPeriodParticipation> + */ + public function getOpenParticipations(): ReadableCollection { return $this ->getParticipations() @@ -860,8 +865,9 @@ class AccompanyingPeriod implements /** * Get the participation containing a person. + * @return ReadableCollection<(int|string), AccompanyingPeriodParticipation> */ - public function getParticipationsContainsPerson(Person $person): Collection + public function getParticipationsContainsPerson(Person $person): ReadableCollection { return $this ->getParticipations() diff --git a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php index a544bd6a4..b463312c8 100644 --- a/src/Bundle/ChillPersonBundle/Entity/Household/Household.php +++ b/src/Bundle/ChillPersonBundle/Entity/Household/Household.php @@ -22,6 +22,7 @@ use DateTimeInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\ReadableCollection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Serializer\Annotation as Serializer; use Symfony\Component\Validator\Constraints as Assert; @@ -265,7 +266,7 @@ class Household * @Serializer\Groups({"read"}) * @Serializer\SerializedName("current_members_id") */ - public function getCurrentMembersIds(?DateTimeImmutable $now = null): Collection + public function getCurrentMembersIds(?DateTimeImmutable $now = null): ReadableCollection { return $this->getCurrentMembers($now)->map( static fn (HouseholdMember $m) => $m->getId() @@ -332,9 +333,9 @@ class Household * * Return a list of Person, instead of a list of HouseholdMembers * - * @return Person[] + * @return ReadableCollection<(int|string), Person> */ - public function getCurrentPersons(?DateTimeImmutable $now = null): Collection + public function getCurrentPersons(?DateTimeImmutable $now = null): ReadableCollection { return $this->getCurrentMembers($now) ->map(static function (HouseholdMember $m) { @@ -358,9 +359,9 @@ class Household /** * get all the members during a given membership. * - * @return Collection|HouseholdMember[] + * @return ReadableCollection<(int|string), HouseholdMember> */ - public function getMembersDuringMembership(HouseholdMember $membership): Collection + public function getMembersDuringMembership(HouseholdMember $membership): ReadableCollection { return $this->getMembersOnRange( $membership->getStartDate(), @@ -384,7 +385,7 @@ class Household return $this->getMembers()->matching($criteria); } - public function getMembersOnRange(DateTimeImmutable $from, ?DateTimeImmutable $to): Collection + public function getMembersOnRange(DateTimeImmutable $from, ?DateTimeImmutable $to): ReadableCollection { return $this->getMembers()->filter(static function (HouseholdMember $m) use ($from, $to) { if (null === $m->getEndDate() && null !== $to) { diff --git a/src/Bundle/ChillPersonBundle/Form/Type/PickPersonDynamicType.php b/src/Bundle/ChillPersonBundle/Form/Type/PickPersonDynamicType.php index 76891a74d..78e382f68 100644 --- a/src/Bundle/ChillPersonBundle/Form/Type/PickPersonDynamicType.php +++ b/src/Bundle/ChillPersonBundle/Form/Type/PickPersonDynamicType.php @@ -28,11 +28,15 @@ class PickPersonDynamicType extends AbstractType { private DenormalizerInterface $denormalizer; - private DenormalizerInterface $normalizer; + private NormalizerInterface $normalizer; private SerializerInterface $serializer; - public function __construct(DenormalizerInterface $denormalizer, SerializerInterface $serializer, NormalizerInterface $normalizer) + public function __construct( + DenormalizerInterface $denormalizer, + SerializerInterface $serializer, + NormalizerInterface $normalizer + ) { $this->denormalizer = $denormalizer; $this->serializer = $serializer; diff --git a/src/Bundle/ChillPersonBundle/Service/DocGenerator/AccompanyingPeriodContext.php b/src/Bundle/ChillPersonBundle/Service/DocGenerator/AccompanyingPeriodContext.php index 3a92df3a4..4a08eff08 100644 --- a/src/Bundle/ChillPersonBundle/Service/DocGenerator/AccompanyingPeriodContext.php +++ b/src/Bundle/ChillPersonBundle/Service/DocGenerator/AccompanyingPeriodContext.php @@ -27,6 +27,7 @@ use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Repository\PersonRepository; use Chill\PersonBundle\Templating\Entity\PersonRenderInterface; use DateTime; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -159,9 +160,9 @@ class AccompanyingPeriodContext implements public function buildPublicForm(FormBuilderInterface $builder, DocGeneratorTemplate $template, mixed $entity): void { $options = $template->getOptions(); - $persons = $entity->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $p) { + $persons = new ArrayCollection($entity->getCurrentParticipations()->map(static function (AccompanyingPeriodParticipation $p) { return $p->getPerson(); - }); + })->toArray()); foreach ($entity->getCurrentParticipations() as $p) { foreach ($p->getPerson()->getResources() as $r) { diff --git a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php index 75ad04cfb..3cdf23f88 100644 --- a/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php +++ b/src/Bundle/ChillPersonBundle/Workflow/AccompanyingPeriodWorkWorkflowHandler.php @@ -102,7 +102,9 @@ class AccompanyingPeriodWorkWorkflowHandler implements EntityWorkflowHandlerInte ->getAccompanyingPeriod() ->getUser(); - $suggestedUsers[spl_object_hash($referrer)] = $referrer; + if (null !== $referrer) { + $suggestedUsers[spl_object_hash($referrer)] = $referrer; + } return $suggestedUsers; } diff --git a/src/Bundle/ChillReportBundle/Export/Export/ReportList.php b/src/Bundle/ChillReportBundle/Export/Export/ReportList.php index e6289c626..21606d807 100644 --- a/src/Bundle/ChillReportBundle/Export/Export/ReportList.php +++ b/src/Bundle/ChillReportBundle/Export/Export/ReportList.php @@ -158,7 +158,7 @@ class ReportList implements ExportElementValidatedInterface, ListInterface ); } - public function getLabels($key, array $values, $data): Closure + public function getLabels($key, array $values, $data) { switch ($key) { case 'person_birthdate': diff --git a/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php b/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php index 058fa119d..6b40f01b9 100644 --- a/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php +++ b/src/Bundle/ChillThirdPartyBundle/DataFixtures/ORM/LoadThirdParty.php @@ -66,22 +66,6 @@ class LoadThirdParty extends Fixture implements DependentFixtureInterface $manager->flush(); } - private function createAddress(): ObjectSet - { - $loader = new NativeLoader(); - - return $loader->loadData([ - Address::class => [ - 'address1' => [ - 'name' => '', - 'telephone' => $this->phoneNumberUtil->getExampleNumber('FR'), - 'email' => '', - 'comment' => '', - ], - ], - ]); - } - private function getCenters(): Iterator { $references = array_map( diff --git a/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php b/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php index 966e40bd1..ca24d2442 100644 --- a/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php +++ b/src/Bundle/ChillThirdPartyBundle/Entity/ThirdParty.php @@ -23,6 +23,7 @@ use DateTimeImmutable; use DateTimeInterface; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; +use Doctrine\Common\Collections\ReadableCollection; use Doctrine\ORM\Mapping as ORM; use libphonenumber\PhoneNumber; use Symfony\Component\Serializer\Annotation\Context; @@ -153,7 +154,7 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface * @ORM\OneToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty", mappedBy="parent", * cascade={"persist"}, orphanRemoval=true) * - * @var Collection|ThirdParty[] + * @var Collection<(int|string), ThirdParty> * @Assert\Valid(traverse=true) */ private Collection $children; @@ -400,7 +401,7 @@ class ThirdParty implements TrackCreationInterface, TrackUpdateInterface /** * Get the children where active = true. */ - public function getActiveChildren(): Collection + public function getActiveChildren(): ReadableCollection { return $this->children->filter(static fn (ThirdParty $tp) => $tp->getActive()); } diff --git a/src/Bundle/ChillWopiBundle/src/Controller/Convert.php b/src/Bundle/ChillWopiBundle/src/Controller/Convert.php index 09fe19678..b3697aafd 100644 --- a/src/Bundle/ChillWopiBundle/src/Controller/Convert.php +++ b/src/Bundle/ChillWopiBundle/src/Controller/Convert.php @@ -94,7 +94,7 @@ class Convert 'Content-Type' => 'application/pdf', ]); } catch (ClientExceptionInterface|TransportExceptionInterface|RedirectionExceptionInterface|ServerExceptionInterface $exception) { - return $this->onConversionFailed($url, $response); + return $this->onConversionFailed($url, $exception->getResponse()); } } diff --git a/tests/app b/tests/app index 5b35e7ccd..5e478fdfb 160000 --- a/tests/app +++ b/tests/app @@ -1 +1 @@ -Subproject commit 5b35e7ccd0735e5593835e28acbf82386c18e1b6 +Subproject commit 5e478fdfbf429baf3ce852ae69eb1f7101b1b416