Add external identifiers for person, editable in edit form, with minimal features associated

This commit is contained in:
2025-09-01 08:05:11 +00:00
parent 76433e2512
commit ea06a96f91
40 changed files with 1274 additions and 128 deletions

View File

@@ -0,0 +1,20 @@
<?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\PersonIdentifier\Exception;
class EngineNotFoundException extends \RuntimeException
{
public function __construct(string $name)
{
parent::__construct("Engine for EngineInterface not found: {$name}");
}
}

View File

@@ -0,0 +1,20 @@
<?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\PersonIdentifier\Exception;
class PersonIdentifierDefinitionNotFoundException extends \RuntimeException
{
public function __construct(int $id, ?\Throwable $previous = null)
{
parent::__construct("Person identifier definition not found by his id: {$id}", previous: $previous);
}
}

View File

@@ -0,0 +1,20 @@
<?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\PersonIdentifier\Exception;
class UnexpectedTypeException extends \InvalidArgumentException
{
public function __construct(mixed $value, string $expectedType)
{
parent::__construct(\sprintf('Expected argument of type "%s", "%s" given', $expectedType, get_debug_type($value)));
}
}

View File

@@ -0,0 +1,41 @@
<?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\PersonIdentifier\Identifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierEngineInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class StringIdentifier implements PersonIdentifierEngineInterface
{
public static function getName(): string
{
return 'chill-person-bundle.string-identifier';
}
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string
{
return $value['content'] ?? '';
}
public function buildForm(FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void
{
$builder->add('content', TextType::class, ['label' => false]);
}
public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string
{
return $identifier?->getValue()['content'] ?? '';
}
}

View File

@@ -0,0 +1,27 @@
<?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\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Symfony\Component\Form\FormBuilderInterface;
interface PersonIdentifierEngineInterface
{
public static function getName(): string;
public function canonicalizeValue(array $value, PersonIdentifierDefinition $definition): ?string;
public function buildForm(FormBuilderInterface $builder, PersonIdentifierDefinition $personIdentifierDefinition): void;
public function renderAsString(?PersonIdentifier $identifier, PersonIdentifierDefinition $definition): string;
}

View File

@@ -0,0 +1,65 @@
<?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\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Chill\PersonBundle\PersonIdentifier\Exception\EngineNotFoundException;
use Chill\PersonBundle\Repository\Identifier\PersonIdentifierDefinitionRepository;
final readonly class PersonIdentifierManager implements PersonIdentifierManagerInterface
{
public function __construct(
private iterable $engines,
private PersonIdentifierDefinitionRepository $personIdentifierDefinitionRepository,
) {}
/**
* Build PersonIdentifierWorker's for all active definition.
*
* @return list<PersonIdentifierWorker>
*/
public function getWorkers(): array
{
$workers = [];
foreach ($this->personIdentifierDefinitionRepository->findByActive() as $definition) {
try {
$worker = $this->getEngine($definition->getEngine());
} catch (EngineNotFoundException) {
continue;
}
$workers[] = new PersonIdentifierWorker($worker, $definition);
}
return $workers;
}
public function buildWorkerByPersonIdentifierDefinition(PersonIdentifierDefinition $personIdentifierDefinition): PersonIdentifierWorker
{
return new PersonIdentifierWorker($this->getEngine($personIdentifierDefinition->getEngine()), $personIdentifierDefinition);
}
/**
* @throw EngineNotFoundException
*/
private function getEngine(string $name): PersonIdentifierEngineInterface
{
foreach ($this->engines as $engine) {
if ($engine->getName() === $name) {
return $engine;
}
}
throw new EngineNotFoundException($name);
}
}

View File

@@ -0,0 +1,26 @@
<?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\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
interface PersonIdentifierManagerInterface
{
/**
* Build PersonIdentifierWorker's for all active definition.
*
* @return list<PersonIdentifierWorker>
*/
public function getWorkers(): array;
public function buildWorkerByPersonIdentifierDefinition(PersonIdentifierDefinition $personIdentifierDefinition): PersonIdentifierWorker;
}

View File

@@ -0,0 +1,49 @@
<?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\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifierDefinition;
use Symfony\Component\Form\FormBuilderInterface;
final readonly class PersonIdentifierWorker
{
public function __construct(
private PersonIdentifierEngineInterface $identifierEngine,
private PersonIdentifierDefinition $definition,
) {}
public function getIdentifierEngine(): PersonIdentifierEngineInterface
{
return $this->identifierEngine;
}
public function getDefinition(): PersonIdentifierDefinition
{
return $this->definition;
}
public function buildForm(FormBuilderInterface $builder): void
{
$this->identifierEngine->buildForm($builder, $this->definition);
}
public function canonicalizeValue(array $value): ?string
{
return $this->identifierEngine->canonicalizeValue($value, $this->definition);
}
public function renderAsString(?PersonIdentifier $identifier): string
{
return $this->identifierEngine->renderAsString($identifier, $this->definition);
}
}

View File

@@ -0,0 +1,65 @@
<?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\PersonIdentifier\Rendering;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
final readonly class PersonIdRendering implements PersonIdRenderingInterface
{
private string $idContentText;
public function __construct(
ParameterBagInterface $parameterBag,
private PersonIdentifierManagerInterface $personIdentifierManager,
) {
$this->idContentText = $parameterBag->get('chill_person')['person_render']['id_content_text'];
}
public function renderPersonId(Person $person): string
{
$args = [
'[[ person_id ]]' => $person->getId(),
];
foreach ($person->getIdentifiers() as $identifier) {
if (!$identifier->getDefinition()->isActive()) {
continue;
}
$key = 'identifier_'.$identifier->getDefinition()->getId();
$args
+= [
"[[ {$key} ]]" => $this->personIdentifierManager->buildWorkerByPersonIdentifierDefinition($identifier->getDefinition())
->renderAsString($identifier),
"[[ if:{$key} ]]" => '',
"[[ endif:{$key} ]]" => '',
];
// we remove the eventual conditions
}
$rendered = strtr($this->idContentText, $args);
// Delete the conditions which are not met, for instance:
// [[ if:identifier_99 ]] ... [[ endif:identifier_99 ]]
// this match the same dumber for opening and closing of the condition
return preg_replace(
'/\[\[\s*if:identifier_(\d+)\s*\]\].*?\[\[\s*endif:identifier_\1\s*\]\]/s',
'',
$rendered
);
}
}

View File

@@ -0,0 +1,19 @@
<?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\PersonIdentifier\Rendering;
use Chill\PersonBundle\Entity\Person;
interface PersonIdRenderingInterface
{
public function renderPersonId(Person $person): string;
}

View File

@@ -0,0 +1,31 @@
<?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\PersonIdentifier\Rendering;
use Chill\PersonBundle\Entity\Person;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
final class PersonIdRenderingTwigExtension extends AbstractExtension
{
public function __construct(private readonly PersonIdRenderingInterface $personIdRendering) {}
public function getFilters(): array
{
return [
new TwigFilter(
'chill_person_id_render_text',
fn (Person $person): string => $this->personIdRendering->renderPersonId($person)
),
];
}
}

View File

@@ -0,0 +1,41 @@
<?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\PersonIdentifier\Rendering;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderInterface;
use Chill\PersonBundle\Entity\Identifier\PersonIdentifier;
use Chill\PersonBundle\PersonIdentifier\PersonIdentifierManagerInterface;
/**
* @template-implements ChillEntityRenderInterface<PersonIdentifier>
*/
final readonly class PersonIdentifierEntityRender implements ChillEntityRenderInterface
{
public function __construct(private PersonIdentifierManagerInterface $identifierManager) {}
public function renderBox(mixed $entity, array $options): string
{
return $this->renderString($entity, $options);
}
public function renderString(mixed $entity, array $options): string
{
$worker = $this->identifierManager->buildWorkerByPersonIdentifierDefinition($entity->getDefinition());
return $worker->renderAsString($entity);
}
public function supports(object $entity, array $options): bool
{
return $entity instanceof PersonIdentifier;
}
}