mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-28 09:34:59 +00:00
Replace value
with canonical
in PersonIdentifier
unique constraint, repository logic, and tests
- Update unique constraint on `PersonIdentifier` to use `canonical` instead of `value`. - Refactor repository method `findByDefinitionAndValue` to `findByDefinitionAndCanonical`, updating logic accordingly. - Adjust validation logic in `UniqueIdentifierConstraintValidator` to align with the new canonical-based approach. - Modify related integration and unit tests to support the changes. - Inject `PersonIdentifierManagerInterface` into the repository to handle canonical value generation.
This commit is contained in:
@@ -17,7 +17,7 @@ use Doctrine\ORM\Mapping as ORM;
|
|||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'chill_person_identifier')]
|
#[ORM\Table(name: 'chill_person_identifier')]
|
||||||
#[ORM\UniqueConstraint(name: 'chill_person_identifier_unique', columns: ['definition_id', 'value'])]
|
#[ORM\UniqueConstraint(name: 'chill_person_identifier_unique', columns: ['definition_id', 'canonical'])]
|
||||||
#[UniqueIdentifierConstraint]
|
#[UniqueIdentifierConstraint]
|
||||||
class PersonIdentifier
|
class PersonIdentifier
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ class PersonIdentifier
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[ORM\ManyToOne(targetEntity: PersonIdentifierDefinition::class)]
|
#[ORM\ManyToOne(targetEntity: PersonIdentifierDefinition::class)]
|
||||||
#[ORM\JoinColumn(name: 'definition_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
|
#[ORM\JoinColumn(name: 'definition_id', referencedColumnName: 'id', nullable: false, onDelete: 'RESTRICT')]
|
||||||
private PersonIdentifierDefinition $definition,
|
private PersonIdentifierDefinition $definition,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@@ -19,9 +19,11 @@ use Symfony\Component\Form\FormBuilderInterface;
|
|||||||
|
|
||||||
final readonly class StringIdentifier implements PersonIdentifierEngineInterface
|
final readonly class StringIdentifier implements PersonIdentifierEngineInterface
|
||||||
{
|
{
|
||||||
|
public const NAME = 'chill-person-bundle.string-identifier';
|
||||||
|
|
||||||
public static function getName(): string
|
public static function getName(): string
|
||||||
{
|
{
|
||||||
return 'chill-person-bundle.string-identifier';
|
return self::NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
|
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
|
||||||
|
@@ -36,7 +36,7 @@ class UniqueIdentifierConstraintValidator extends ConstraintValidator
|
|||||||
throw new UnexpectedValueException($value, PersonIdentifier::class);
|
throw new UnexpectedValueException($value, PersonIdentifier::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
$identifiers = $this->personIdentifierRepository->findByDefinitionAndValue($value->getDefinition(), $value->getValue());
|
$identifiers = $this->personIdentifierRepository->findByDefinitionAndCanonical($value->getDefinition(), $value->getValue());
|
||||||
|
|
||||||
if (count($identifiers) > 0) {
|
if (count($identifiers) > 0) {
|
||||||
$persons = array_map(fn (PersonIdentifier $idf): string => $this->personRender->renderString($idf->getPerson(), []), $identifiers);
|
$persons = array_map(fn (PersonIdentifier $idf): string => $this->personRender->renderString($idf->getPerson(), []), $identifiers);
|
||||||
|
@@ -13,24 +13,29 @@ namespace Chill\PersonBundle\Repository\Identifier;
|
|||||||
|
|
||||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||||
|
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\DBAL\Types\Types;
|
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
class PersonIdentifierRepository extends ServiceEntityRepository
|
class PersonIdentifierRepository extends ServiceEntityRepository
|
||||||
{
|
{
|
||||||
public function __construct(ManagerRegistry $registry)
|
public function __construct(ManagerRegistry $registry, private readonly PersonIdentifierManagerInterface $personIdentifierManager)
|
||||||
{
|
{
|
||||||
parent::__construct($registry, PersonIdentifier::class);
|
parent::__construct($registry, PersonIdentifier::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findByDefinitionAndValue(PersonIdentifierDefinition $definition, array $value): array
|
public function findByDefinitionAndCanonical(PersonIdentifierDefinition $definition, array|string $valueOrCanonical): array
|
||||||
{
|
{
|
||||||
return $this->createQueryBuilder('p')
|
return $this->createQueryBuilder('p')
|
||||||
->where('p.definition = :definition')
|
->where('p.definition = :definition')
|
||||||
->andWhere('p.value = :value')
|
->andWhere('p.canonical = :canonical')
|
||||||
->setParameter('definition', $definition)
|
->setParameter('definition', $definition)
|
||||||
->setParameter('value', $value, Types::JSON)
|
->setParameter(
|
||||||
|
'canonical',
|
||||||
|
is_string($valueOrCanonical) ?
|
||||||
|
$valueOrCanonical :
|
||||||
|
$this->personIdentifierManager->buildWorkerByPersonIdentifierDefinition($definition)->canonicalizeValue($valueOrCanonical),
|
||||||
|
)
|
||||||
->getQuery()
|
->getQuery()
|
||||||
->getResult();
|
->getResult();
|
||||||
}
|
}
|
||||||
|
@@ -82,7 +82,7 @@ final class UniqueIdentifierConstraintValidatorTest extends ConstraintValidatorT
|
|||||||
$identifier->setValue(['value' => 'UNIQ']);
|
$identifier->setValue(['value' => 'UNIQ']);
|
||||||
|
|
||||||
// Configure repository mock to return empty array
|
// Configure repository mock to return empty array
|
||||||
$this->repository->findByDefinitionAndValue($definition, ['value' => 'UNIQ'])->willReturn([]);
|
$this->repository->findByDefinitionAndCanonical($definition, ['value' => 'UNIQ'])->willReturn([]);
|
||||||
|
|
||||||
$this->validator->validate($identifier, new UniqueIdentifierConstraint());
|
$this->validator->validate($identifier, new UniqueIdentifierConstraint());
|
||||||
$this->assertNoViolation();
|
$this->assertNoViolation();
|
||||||
@@ -108,7 +108,7 @@ final class UniqueIdentifierConstraintValidatorTest extends ConstraintValidatorT
|
|||||||
$dup2->setValue(['value' => '123']);
|
$dup2->setValue(['value' => '123']);
|
||||||
|
|
||||||
// Repository returns duplicates
|
// Repository returns duplicates
|
||||||
$this->repository->findByDefinitionAndValue($definition, ['value' => '123'])->willReturn([$dup1, $dup2]);
|
$this->repository->findByDefinitionAndCanonical($definition, ['value' => '123'])->willReturn([$dup1, $dup2]);
|
||||||
|
|
||||||
// Person renderer returns names
|
// Person renderer returns names
|
||||||
$this->personRender->renderString($personA, Argument::type('array'))->willReturn('Alice Anderson');
|
$this->personRender->renderString($personA, Argument::type('array'))->willReturn('Alice Anderson');
|
||||||
|
@@ -14,6 +14,8 @@ namespace Chill\PersonBundle\Tests\Repository\Identifier;
|
|||||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
|
||||||
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\PersonBundle\PersonIdentifier\Identifier\StringIdentifier;
|
||||||
|
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
|
||||||
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierRepository;
|
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierRepository;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
@@ -25,10 +27,12 @@ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
|||||||
*/
|
*/
|
||||||
class PersonIdentifierRepositoryTest extends KernelTestCase
|
class PersonIdentifierRepositoryTest extends KernelTestCase
|
||||||
{
|
{
|
||||||
public function testFindByDefinitionAndValue(): void
|
public function testFindByDefinitionAndCanonical(): void
|
||||||
{
|
{
|
||||||
self::bootKernel();
|
self::bootKernel();
|
||||||
$container = self::getContainer();
|
$container = self::getContainer();
|
||||||
|
/** @var PersonIdentifierManagerInterface $personIdentifierManager */
|
||||||
|
$personIdentifierManager = $container->get(PersonIdentifierManagerInterface::class);
|
||||||
|
|
||||||
/** @var EntityManagerInterface $em */
|
/** @var EntityManagerInterface $em */
|
||||||
$em = $container->get(EntityManagerInterface::class);
|
$em = $container->get(EntityManagerInterface::class);
|
||||||
@@ -39,23 +43,23 @@ class PersonIdentifierRepositoryTest extends KernelTestCase
|
|||||||
self::assertNotNull($person, 'An existing Person is required for this integration test.');
|
self::assertNotNull($person, 'An existing Person is required for this integration test.');
|
||||||
|
|
||||||
// Create a definition
|
// Create a definition
|
||||||
$definition = new PersonIdentifierDefinition(['en' => 'Test Identifier'], 'string');
|
$definition = new PersonIdentifierDefinition(['en' => 'Test Identifier'], StringIdentifier::NAME);
|
||||||
$em->persist($definition);
|
$em->persist($definition);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
// Create an identifier attached to the person
|
// Create an identifier attached to the person
|
||||||
$value = ['value' => 'ABC-'.bin2hex(random_bytes(4))];
|
$value = ['content' => 'ABC-'.bin2hex(random_bytes(4))];
|
||||||
$identifier = new PersonIdentifier($definition);
|
$identifier = new PersonIdentifier($definition);
|
||||||
$identifier->setPerson($person);
|
$identifier->setPerson($person);
|
||||||
$identifier->setValue($value);
|
$identifier->setValue($value);
|
||||||
$identifier->setCanonical('canonical-'.$value['value']);
|
$identifier->setCanonical($personIdentifierManager->buildWorkerByPersonIdentifierDefinition($definition)->canonicalizeValue($identifier->getValue()));
|
||||||
$em->persist($identifier);
|
$em->persist($identifier);
|
||||||
$em->flush();
|
$em->flush();
|
||||||
|
|
||||||
// Use the repository to find by definition and value
|
// Use the repository to find by definition and value
|
||||||
/** @var PersonIdentifierRepository $repo */
|
/** @var PersonIdentifierRepository $repo */
|
||||||
$repo = $container->get(PersonIdentifierRepository::class);
|
$repo = $container->get(PersonIdentifierRepository::class);
|
||||||
$results = $repo->findByDefinitionAndValue($definition, $value);
|
$results = $repo->findByDefinitionAndCanonical($definition, $value);
|
||||||
|
|
||||||
self::assertNotEmpty($results, 'Repository should return at least one result.');
|
self::assertNotEmpty($results, 'Repository should return at least one result.');
|
||||||
self::assertContainsOnlyInstancesOf(PersonIdentifier::class, $results);
|
self::assertContainsOnlyInstancesOf(PersonIdentifier::class, $results);
|
||||||
|
@@ -23,7 +23,7 @@ final class Version20250922151020 extends AbstractMigration
|
|||||||
|
|
||||||
public function up(Schema $schema): void
|
public function up(Schema $schema): void
|
||||||
{
|
{
|
||||||
$this->addSql('CREATE UNIQUE INDEX chill_person_identifier_unique ON chill_person_identifier (definition_id, value)');
|
$this->addSql('CREATE UNIQUE INDEX chill_person_identifier_unique ON chill_person_identifier (definition_id, canonical)');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(Schema $schema): void
|
public function down(Schema $schema): void
|
||||||
|
Reference in New Issue
Block a user