Refactor normalizers: improve null handling, enhance normalization logic, add dedicated tests, and update supported types.

This commit is contained in:
2026-01-06 17:41:19 +01:00
parent 82821edb2a
commit 1dfebc0297
9 changed files with 272 additions and 59 deletions

View File

@@ -11,9 +11,14 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\SocialWork\Evaluation;
use Doctrine\Common\Collections\Collection;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
@@ -24,6 +29,22 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements NormalizerInterface,
{
use NormalizerAwareTrait;
private const array EVALUATION_NULL = [
'id',
'comment',
'createdAt' => \DateTimeImmutable::class,
'createdBy' => User::class,
'endDate' => \DateTimeImmutable::class,
'evaluation' => Evaluation::class,
'maxDate' => \DateTimeImmutable::class,
'startDate' => \DateTimeImmutable::class,
'updatedAt' => \DateTimeImmutable::class,
'updatedBy' => User::class,
'timeSpent',
'key',
'warningInterval' => \DateInterval::class,
];
public function __construct(
private readonly Registry $registry,
private readonly EntityWorkflowRepository $entityWorkflowRepository,
@@ -32,7 +53,13 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements NormalizerInterface,
public function normalize($object, ?string $format = null, array $context = []): array
{
if (!$object instanceof AccompanyingPeriodWorkEvaluation) {
if ('docgen' === $format && null === $object) {
return $this->normalizeNull($format, $context);
}
if (
('json' === $format && !$object instanceof AccompanyingPeriodWorkEvaluation)
|| ('docgen' === $format && !$object instanceof AccompanyingPeriodWorkEvaluation)
) {
throw new UnexpectedValueException('expected AccompanyingPeriodWorkEvaluation instance, got '.gettype($object));
}
@@ -62,10 +89,26 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements NormalizerInterface,
$initial['workflows'] = $this->normalizer->normalize($workflows, 'json', $context);
$initial['type'] = 'accompanying_period_work_evaluation';
$initial['isNull'] = false;
return $initial;
}
private function normalizeNull(?string $format, array $context): array
{
return array_merge(
(new NormalizeNullValueHelper($this->normalizer, 'type', 'accompanying_period_work_evaluation'))
->normalize(self::EVALUATION_NULL, $format, $context),
[
'workflows_availables' => $this->metadataExtractor->availableWorkflowFor(
AccompanyingPeriodWorkEvaluation::class,
),
'workflows' => [],
'documents' => [],
]
);
}
private function normalizeProperties(AccompanyingPeriodWorkEvaluation $object, ?string $format, array $context): array
{
$data = [];
@@ -74,29 +117,34 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements NormalizerInterface,
$isRead = in_array('read', $groups, true);
$isDocgenRead = in_array('docgen:read', $groups, true);
$dateContext = array_merge($context, ['docgen:expects' => \DateTimeImmutable::class]);
$userContext = array_merge($context, ['docgen:expects' => User::class]);
$evaluationContext = array_merge($context, ['docgen:expects' => Evaluation::class]);
if ($isRead || $isDocgenRead) {
$data['id'] = $object->getId();
$data['comment'] = $object->getComment();
$data['createdAt'] = $this->normalizer->normalize($object->getCreatedAt(), $format, $context);
$data['createdBy'] = $this->normalizer->normalize($object->getCreatedBy(), $format, $context);
$data['endDate'] = $this->normalizer->normalize($object->getEndDate(), $format, $context);
$data['evaluation'] = $this->normalizer->normalize($object->getEvaluation(), $format, $context);
$data['maxDate'] = $this->normalizer->normalize($object->getMaxDate(), $format, $context);
$data['startDate'] = $this->normalizer->normalize($object->getStartDate(), $format, $context);
$data['updatedAt'] = $this->normalizer->normalize($object->getUpdatedAt(), $format, $context);
$data['updatedBy'] = $this->normalizer->normalize($object->getUpdatedBy(), $format, $context);
$data['createdAt'] = $this->normalizer->normalize($object->getCreatedAt(), $format, $dateContext);
$data['createdBy'] = $this->normalizer->normalize($object->getCreatedBy(), $format, $userContext);
$data['endDate'] = $this->normalizer->normalize($object->getEndDate(), $format, $dateContext);
$data['evaluation'] = $this->normalizer->normalize($object->getEvaluation(), $format, $evaluationContext);
$data['maxDate'] = $this->normalizer->normalize($object->getMaxDate(), $format, $dateContext);
$data['startDate'] = $this->normalizer->normalize($object->getStartDate(), $format, $dateContext);
$data['updatedAt'] = $this->normalizer->normalize($object->getUpdatedAt(), $format, $dateContext);
$data['updatedBy'] = $this->normalizer->normalize($object->getUpdatedBy(), $format, $userContext);
$data['timeSpent'] = $object->getTimeSpent();
}
if ($isRead) {
if ($isRead || $isDocgenRead) {
$data['key'] = $object->getKey();
$data['warningInterval'] = $this->normalizer->normalize($object->getWarningInterval(), $format, $context);
$data['warningInterval'] = $this->normalizer->normalize($object->getWarningInterval(), $format, array_merge($context, ['docgen:expects' => \DateInterval::class]));
}
// Handle 'read:evaluation:include-work' group for accompanyingPeriodWork
if (in_array('read:evaluation:include-work', $groups, true)) {
$workContext = $context;
$workContext['groups'] = ['read:accompanyingPeriodWork:light'];
$workContext['docgen:expects'] = AccompanyingPeriodWork::class;
$data['accompanyingPeriodWork'] = $this->normalizer->normalize($object->getAccompanyingPeriodWork(), $format, $workContext);
}
@@ -105,10 +153,6 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements NormalizerInterface,
public function supportsNormalization($data, ?string $format = null, array $context = []): bool
{
if ('json' !== $format || !$data instanceof AccompanyingPeriodWorkEvaluation) {
return false;
}
$groups = (array) ($context['groups'] ?? []);
foreach (['write', 'accompanying_period_work_evaluation:create'] as $forbiddenGroup) {
if (in_array($forbiddenGroup, $groups, true)) {
@@ -116,11 +160,23 @@ class AccompanyingPeriodWorkEvaluationNormalizer implements NormalizerInterface,
}
}
return true;
if ('docgen' === $format) {
return $data instanceof AccompanyingPeriodWorkEvaluation
|| (null === $data && AccompanyingPeriodWorkEvaluation::class === $context['docgen:expects']);
}
if ('json' === $format && $data instanceof AccompanyingPeriodWorkEvaluation) {
return true;
}
return false;
}
public function getSupportedTypes(?string $format): array
{
return 'json' === $format ? [AccompanyingPeriodWorkEvaluation::class => true] : [];
return match ($format) {
'json' => [AccompanyingPeriodWorkEvaluation::class => true],
'docgen' => [AccompanyingPeriodWorkEvaluation::class => true, '*' => false],
};
}
}

View File

@@ -15,10 +15,12 @@ use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Serializer\Normalizer\UserNormalizer;
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\SocialWork\Goal;
use Chill\PersonBundle\Entity\SocialWork\Result;
use Chill\PersonBundle\Entity\SocialWork\SocialAction;
use Chill\ThirdPartyBundle\Entity\ThirdParty;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@@ -55,7 +57,7 @@ class AccompanyingPeriodWorkNormalizer implements \Symfony\Component\Serializer\
return [
'isNull' => true,
'type' => 'accompanying_period_work',
'accompanyingPeriodWorkEvaluations' => [],
'evaluations' => [],
'referrers' => [],
'createdAt' => $dateNull,
'createdAutomatically' => false,
@@ -64,7 +66,7 @@ class AccompanyingPeriodWorkNormalizer implements \Symfony\Component\Serializer\
'endDate' => $dateNull,
'goals' => [],
'handlingThierParty' => $this->normalizer->normalize(null, $format, [...$cleanContext, 'docgen:expects' => ThirdParty::class]),
'id' => null,
'id' => '',
'note' => '',
'persons' => [],
'results' => [],
@@ -73,7 +75,6 @@ class AccompanyingPeriodWorkNormalizer implements \Symfony\Component\Serializer\
'thirdParties' => [],
'updatedAt' => $dateNull,
'updatedBy' => $userNull,
'accompanyingPeriod' => $this->normalizer->normalize(null, $format, [...$cleanContext, 'docgen:expects' => AccompanyingPeriod::class]),
'workflows_availables' => [],
'workflows_availables_evaluation' => [],
'workflows_availables_evaluation_documents' => [],
@@ -132,13 +133,35 @@ class AccompanyingPeriodWorkNormalizer implements \Symfony\Component\Serializer\
return [];
}
if (
in_array('read:accompanyingPeriodWork:light', $groups, true)
|| in_array('read', $groups, true)
) {
$context = [...$context, 'groups' => ['read']];
$dateContext = $context;
$personContext = $context;
$socialActionContext = $context;
$goalContext = $context;
$resultContext = $context;
$thirdPartyContext = $context;
} elseif (in_array('docgen:read', $groups, true)) {
$dateContext = [...$context, 'docgen:expects' => \DateTimeInterface::class];
$personContext = [...$context, 'docgen:expects' => Person::class];
$socialActionContext = [...$context, 'docgen:expects' => SocialAction::class];
$goalContext = [...$context, 'docgen:expects' => Goal::class];
$resultContext = [...$context, 'docgen:expects' => Result::class];
$thirdPartyContext = [...$context, 'docgen:expects' => ThirdParty::class];
} else {
throw new \InvalidArgumentException('Invalid group for normalizing accompanying period work');
}
$data['createdAutomatically'] = $object->getCreatedAutomatically();
$data['createdAutomaticallyReason'] = $object->getCreatedAutomaticallyReason();
$data['endDate'] = $this->normalizer->normalize($object->getEndDate(), $format, $context);
$data['endDate'] = $this->normalizer->normalize($object->getEndDate(), $format, $dateContext);
$data['id'] = $object->getId();
$data['persons'] = $this->normalizer->normalize($object->getPersons()->getValues(), $format, $context);
$data['socialAction'] = $this->normalizer->normalize($object->getSocialAction(), $format, [...$context, 'groups' => ['read']]);
$data['startDate'] = $this->normalizer->normalize($object->getStartDate(), $format, $context);
$data['persons'] = $this->normalizer->normalize($object->getPersons()->getValues(), $format, $personContext);
$data['socialAction'] = $this->normalizer->normalize($object->getSocialAction(), $format, $socialActionContext);
$data['startDate'] = $this->normalizer->normalize($object->getStartDate(), $format, $dateContext);
if (in_array('read:accompanyingPeriodWork:light', $groups, true)) {
$data['referrers'] = []; // Include referrers for consistency
@@ -157,11 +180,11 @@ class AccompanyingPeriodWorkNormalizer implements \Symfony\Component\Serializer\
}
if (in_array('read', $groups, true) || in_array('docgen:read', $groups, true)) {
$data['goals'] = $this->normalizer->normalize($object->getGoals()->getValues(), $format, $context);
$data['handlingThierParty'] = $this->normalizer->normalize($object->getHandlingThierParty(), $format, $context);
$data['goals'] = $this->normalizer->normalize($object->getGoals()->getValues(), $format, $goalContext);
$data['handlingThierParty'] = $this->normalizer->normalize($object->getHandlingThierParty(), $format, $thirdPartyContext);
$data['note'] = $object->getNote();
$data['results'] = $this->normalizer->normalize($object->getResults()->getValues(), $format, $context);
$data['thirdParties'] = $this->normalizer->normalize($object->getThirdParties()->getValues(), $format, $context);
$data['results'] = $this->normalizer->normalize($object->getResults()->getValues(), $format, $resultContext);
$data['thirdParties'] = $this->normalizer->normalize($object->getThirdParties()->getValues(), $format, $thirdPartyContext);
}
if (in_array('read', $groups, true)) {
@@ -175,16 +198,15 @@ class AccompanyingPeriodWorkNormalizer implements \Symfony\Component\Serializer\
}
if (in_array('docgen:read', $groups, true)) {
$data['accompanyingPeriod'] = $this->normalizer->normalize($object->getAccompanyingPeriod(), $format, [...$context, 'groups' => ['read']]);
$data['accompanyingPeriodWorkEvaluations'] = $this->normalizer->normalize(
$data['evaluations'] = $this->normalizer->normalize(
$object->getAccompanyingPeriodWorkEvaluations()->getValues(),
$format,
$context
[...$context, 'docgen:expects' => AccompanyingPeriodWorkEvaluation::class]
);
$data['createdAt'] = $this->normalizer->normalize($object->getCreatedAt(), $format, $context);
$data['createdBy'] = $this->normalizer->normalize($object->getCreatedBy(), $format, $context);
$data['updatedAt'] = $this->normalizer->normalize($object->getUpdatedAt(), $format, $context);
$data['updatedBy'] = $this->normalizer->normalize($object->getUpdatedBy(), $format, $context);
$data['createdAt'] = $this->normalizer->normalize($object->getCreatedAt(), $format, $dateContext);
$data['createdBy'] = $this->normalizer->normalize($object->getCreatedBy(), $format, [...$context, 'docgen:expects' => User::class]);
$data['updatedAt'] = $this->normalizer->normalize($object->getUpdatedAt(), $format, $dateContext);
$data['updatedBy'] = $this->normalizer->normalize($object->getUpdatedBy(), $format, [...$context, 'docgen:expects' => User::class]);
$data['referrers'] = []; // Include referrers in properties for key consistency
$data['workflows_availables'] = [];
$data['workflows_availables_evaluation'] = [];

View File

@@ -38,14 +38,6 @@ final class AccompanyingPeriodWorkDocGenNormalizerTest extends DocGenNormalizerT
{
use ProphecyTrait;
private NormalizerInterface $normalizer;
protected function setUp(): void
{
parent::bootKernel();
$this->normalizer = self::getContainer()->get(NormalizerInterface::class);
}
public function testNormalizeNullObject(): void
{
$nullNormalizedObject = $this->getNormalizer()->normalize(null, 'docgen', [
@@ -77,11 +69,6 @@ final class AccompanyingPeriodWorkDocGenNormalizerTest extends DocGenNormalizerT
return AccompanyingPeriodWork::class;
}
public function getNormalizer(): NormalizerInterface
{
return $this->normalizer;
}
public function testSupportsNormalization(): void
{
$registry = $this->prophesize(Registry::class);

View File

@@ -0,0 +1,48 @@
<?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\Tests\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Test\DocGenNormalizerTestAbstract;
use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluation;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingPeriodWorkEvaluationDocGenNormalizerTest extends DocGenNormalizerTestAbstract
{
public function provideNotNullObject(): object
{
$accompanyingPeriod = new \Chill\PersonBundle\Entity\AccompanyingPeriod(new \DateTime('2024-01-01'));
$work = new \Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork();
$work->setStartDate(new \DateTime('2024-01-01'));
$work->setAccompanyingPeriod($accompanyingPeriod);
$evaluationType = new \Chill\PersonBundle\Entity\SocialWork\Evaluation();
$evaluationType->setTitle(['en' => 'Monthly Evaluation']);
$evaluation = new AccompanyingPeriodWorkEvaluation();
$evaluation->setAccompanyingPeriodWork($work);
$evaluation->setEvaluation($evaluationType);
$evaluation->setStartDate(new \DateTimeImmutable('2024-01-01'));
$evaluation->setEndDate(new \DateTimeImmutable('2024-01-31'));
return $evaluation;
}
public function provideDocGenExpectClass(): string
{
return AccompanyingPeriodWorkEvaluation::class;
}
}