Fixed: fix docgen normalization on household with "old" members

When a household had old members, the indexes of each "current" members
should be numerical and contiguous, to be transformed in a list. If this
is not the case, the members are mapped to an associative array.

This commit alter the generic DocGenObjectNormalizer to ensure that
the ReadableCollection are normalized using the
CollectionDocGenNormalizer as default, which do not preserve keys.
This commit is contained in:
Julien Fastré 2023-05-16 23:24:33 +02:00
parent 1abaf2acb0
commit 8a684734e7
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
4 changed files with 117 additions and 3 deletions

View File

@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
use ArrayObject; use ArrayObject;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ReadableCollection;
use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
@ -51,7 +52,9 @@ class CollectionDocGenNormalizer implements ContextAwareNormalizerInterface, Nor
return false; return false;
} }
return $data instanceof Collection return $data instanceof ReadableCollection
|| (null === $data && Collection::class === ($context['docgen:expects'] ?? null)); || (null === $data && Collection::class === ($context['docgen:expects'] ?? null))
|| (null === $data && ReadableCollection::class === ($context['docgen:expects'] ?? null))
;
} }
} }

View File

@ -13,6 +13,7 @@ namespace Chill\DocGeneratorBundle\Serializer\Normalizer;
use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper; use Chill\DocGeneratorBundle\Serializer\Helper\NormalizeNullValueHelper;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface; use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Doctrine\Common\Collections\ReadableCollection;
use ReflectionClass; use ReflectionClass;
use RuntimeException; use RuntimeException;
use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccess;
@ -271,6 +272,14 @@ class DocGenObjectNormalizer implements NormalizerAwareInterface, NormalizerInte
if ($isTranslatable) { if ($isTranslatable) {
$data[$key] = $this->translatableStringHelper $data[$key] = $this->translatableStringHelper
->localize($value); ->localize($value);
} elseif ($value instanceof ReadableCollection) {
// when normalizing collection, we should not preserve keys (to ensure that the result is a list)
// this is why we make call to the normalizer again to use the CollectionDocGenNormalizer
$data[$key] =
$this->normalizer->normalize($value, $format, array_merge(
$objectContext,
$attribute->getNormalizationContextForGroups($expectedGroups)
));
} elseif (is_iterable($value)) { } elseif (is_iterable($value)) {
$arr = []; $arr = [];

View File

@ -0,0 +1,52 @@
<?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\DocGeneratorBundle\tests\Serializer\Normalizer;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/**
* @internal
* @coversNothing
*/
class CollectionDocGenNormalizerTest extends KernelTestCase
{
private NormalizerInterface $normalizer;
protected function setUp(): void
{
self::bootKernel();
$this->normalizer = self::$container->get(NormalizerInterface::class);
}
public function testNormalizeFilteredArray(): void
{
$coll = new ArrayCollection([
(object) ['v' => 'foo'],
(object) ['v' => 'bar'],
(object) ['v' => 'baz'],
]);
//filter to get non continuous indexes
$criteria = new Criteria();
$criteria->where(Criteria::expr()->neq('v', 'bar'));
$filtered = $coll->matching($criteria);
$normalized = $this->normalizer->normalize($filtered, 'docgen', []);
self::assertIsArray($normalized);
self::assertArrayHasKey(0, $normalized);
self::assertArrayHasKey(1, $normalized);
}
}

View File

@ -18,6 +18,7 @@ use Chill\PersonBundle\Entity\Person;
use DateTimeImmutable; use DateTimeImmutable;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
/** /**
@ -39,7 +40,7 @@ final class HouseholdNormalizerTest extends KernelTestCase
$this->entityManager = self::$container->get(EntityManagerInterface::class); $this->entityManager = self::$container->get(EntityManagerInterface::class);
} }
public function testNormalizationRecursive() public function testNormalizationRecursive(): void
{ {
$person = new Person(); $person = new Person();
$person->setFirstName('ok')->setLastName('ok'); $person->setFirstName('ok')->setLastName('ok');
@ -67,4 +68,53 @@ final class HouseholdNormalizerTest extends KernelTestCase
$this->assertArrayHasKey('type', $normalized); $this->assertArrayHasKey('type', $normalized);
$this->assertEquals('household', $normalized['type']); $this->assertEquals('household', $normalized['type']);
} }
/**
* When a household have old members (members which are not "current"),
* the indexes of the household must be reset to numerical and contiguous
* indexes. This ensure that it will be mapped as a list, not as an associative
* array.
*/
public function testHouseholdDocGenNormalizationWithOldMembers(): void
{
$previousPerson = new Person();
$previousPerson->setFirstName('ok')->setLastName('ok');
$this->entityManager->persist($previousPerson);
$member = new HouseholdMember();
$household = new Household();
$position = (new Position())
->setShareHousehold(true)
->setAllowHolder(true);
$member->setPerson($previousPerson)
->setStartDate(new DateTimeImmutable('1 year ago'))
->setEndDate(new DateTimeImmutable('1 month ago'))
->setPosition($position);
$household->addMember($member);
$currentPerson1 = new Person();
$currentPerson1->setFirstName('p1')->setLastName('p1');
$this->entityManager->persist($currentPerson1);
$member = new HouseholdMember();
$member->setPerson($currentPerson1)
->setStartDate(new DateTimeImmutable('1 year ago'))
->setPosition($position);
$household->addMember($member);
$normalized = $this->normalizer->normalize(
$household,
'docgen',
[
AbstractNormalizer::GROUPS => ['docgen:read'],
'docgen:expects' => Household::class,
'docgen:person:with-household' => false,
'docgen:person:with-relations' => false,
'docgen:person:with-budget' => false,
]
);
self::assertIsArray($normalized);
self::assertArrayHasKey(0, $normalized['currentMembers']);
}
} }