Add audit functionality for AccompanyingCourseDocument and integrate subject converter and displayer

- Introduced `AccompanyingCourseDocumentSubjectConverter` for audit conversion logic.
- Added `AccompanyingCourseDocumentSubjectDisplayer` for handling audit display logic, including a Twig template.
- Created unit tests to verify proper behavior of the converter and displayer.
This commit is contained in:
2026-03-02 13:31:16 +01:00
parent 4c8eb4b3b9
commit 2c252fa79a
6 changed files with 274 additions and 0 deletions

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\DocStoreBundle\Audit\Displayer;
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
use Chill\MainBundle\Audit\Subject;
use Chill\MainBundle\Audit\SubjectDisplayerInterface;
use Twig\Environment;
final class AccompanyingCourseDocumentSubjectDisplayer implements SubjectDisplayerInterface
{
public function __construct(
private readonly AccompanyingCourseDocumentRepository $repository,
private readonly Environment $twig,
) {}
public function supportsDisplay(Subject $subject, array $options = []): bool
{
return 'accompanying_course_document' === $subject->type;
}
public function display(Subject $subject, string $format = 'html', array $options = []): string
{
$id = $subject->identifiers['id'];
$document = $this->repository->find($id);
if ('html' === $format) {
return $this->twig->render('@ChillDocStore/Audit/accompanying_course_document.html.twig', [
'id' => $id,
'document' => $document,
]);
}
if (null === $document) {
return (string) $id;
}
return $document->getTitle();
}
}

View File

@@ -0,0 +1,53 @@
<?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\DocStoreBundle\Audit\SubjectConverter;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\MainBundle\Audit\Subject;
use Chill\MainBundle\Audit\SubjectBag;
use Chill\MainBundle\Audit\SubjectConverterInterface;
use Chill\MainBundle\Audit\SubjectConverterManagerAwareInterface;
use Chill\MainBundle\Audit\SubjectConverterManagerAwareTrait;
/**
* @implements SubjectConverterInterface<AccompanyingCourseDocument>
*/
final class AccompanyingCourseDocumentSubjectConverter implements SubjectConverterInterface, SubjectConverterManagerAwareInterface
{
use SubjectConverterManagerAwareTrait;
public function supportsConvert(mixed $subject): bool
{
return $subject instanceof AccompanyingCourseDocument;
}
public function convert(mixed $subject, bool $includeAssociated = false): SubjectBag
{
$main = new SubjectBag(
new Subject(
type: 'accompanying_course_document',
identifiers: ['id' => $subject->getId()],
)
);
if (null !== $subject->getCourse()) {
$main->append($this->subjectConverterManager->getSubjectsForEntity($subject->getCourse(), $includeAssociated));
}
return $main;
}
public static function getDefaultPriority(): int
{
return 0;
}
}

View File

@@ -0,0 +1 @@
<span>{{ 'audit.accompanying_course_document.display'|trans({'{id}': id }) }}{% if document is not null %} - {{ document.title }}{% endif %}</span>

View File

@@ -0,0 +1,94 @@
<?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\DocStoreBundle\Tests\Audit\Displayer;
use Chill\DocStoreBundle\Audit\Displayer\AccompanyingCourseDocumentSubjectDisplayer;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Repository\AccompanyingCourseDocumentRepository;
use Chill\MainBundle\Audit\Subject;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Twig\Environment;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingCourseDocumentSubjectDisplayerTest extends TestCase
{
use ProphecyTrait;
public function testSupportsDisplay(): void
{
$repository = $this->prophesize(AccompanyingCourseDocumentRepository::class);
$twig = $this->prophesize(Environment::class);
$displayer = new AccompanyingCourseDocumentSubjectDisplayer($repository->reveal(), $twig->reveal());
$this->assertTrue($displayer->supportsDisplay(new Subject('accompanying_course_document', ['id' => 123])));
$this->assertFalse($displayer->supportsDisplay(new Subject('other', ['id' => 123])));
}
public function testDisplayHtml(): void
{
$repository = $this->prophesize(AccompanyingCourseDocumentRepository::class);
$twig = $this->prophesize(Environment::class);
$document = $this->prophesize(AccompanyingCourseDocument::class);
$id = 123;
$subject = new Subject('accompanying_course_document', ['id' => $id]);
$repository->find($id)->willReturn($document->reveal());
$twig->render('@ChillDocStore/Audit/accompanying_course_document.html.twig', [
'id' => $id,
'document' => $document->reveal(),
])->willReturn('<span>Document Title</span>');
$displayer = new AccompanyingCourseDocumentSubjectDisplayer($repository->reveal(), $twig->reveal());
$result = $displayer->display($subject, 'html');
$this->assertSame('<span>Document Title</span>', $result);
}
public function testDisplayString(): void
{
$repository = $this->prophesize(AccompanyingCourseDocumentRepository::class);
$twig = $this->prophesize(Environment::class);
$document = $this->prophesize(AccompanyingCourseDocument::class);
$id = 123;
$subject = new Subject('accompanying_course_document', ['id' => $id]);
$document->getTitle()->willReturn('Document Title');
$repository->find($id)->willReturn($document->reveal());
$displayer = new AccompanyingCourseDocumentSubjectDisplayer($repository->reveal(), $twig->reveal());
$result = $displayer->display($subject, 'string');
$this->assertSame('Document Title', $result);
}
public function testDisplayWithIdFallback(): void
{
$repository = $this->prophesize(AccompanyingCourseDocumentRepository::class);
$twig = $this->prophesize(Environment::class);
$id = 123;
$subject = new Subject('accompanying_course_document', ['id' => $id]);
$repository->find($id)->willReturn(null);
$displayer = new AccompanyingCourseDocumentSubjectDisplayer($repository->reveal(), $twig->reveal());
$result = $displayer->display($subject, 'string');
$this->assertSame('123', $result);
}
}

View File

@@ -0,0 +1,72 @@
<?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\DocStoreBundle\Tests\Audit\SubjectConverter;
use Chill\DocStoreBundle\Audit\SubjectConverter\AccompanyingCourseDocumentSubjectConverter;
use Chill\DocStoreBundle\Entity\AccompanyingCourseDocument;
use Chill\DocStoreBundle\Entity\StoredObject;
use Chill\MainBundle\Audit\Subject;
use Chill\MainBundle\Audit\SubjectBag;
use Chill\MainBundle\Audit\SubjectConverterManagerInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
/**
* @internal
*
* @coversNothing
*/
class AccompanyingCourseDocumentSubjectConverterTest extends TestCase
{
use ProphecyTrait;
public function testSupportsConvert(): void
{
$converter = new AccompanyingCourseDocumentSubjectConverter();
$this->assertTrue($converter->supportsConvert($this->createMock(AccompanyingCourseDocument::class)));
$this->assertFalse($converter->supportsConvert(new \stdClass()));
}
public function testConvert(): void
{
$storedObject = new StoredObject();
$storedObject->setTitle('Document Title');
$document = new AccompanyingCourseDocument();
$document->setObject($storedObject);
$reflection = new \ReflectionClass($document);
$idProp = $reflection->getProperty('id');
$idProp->setValue($document, 123);
$course = new AccompanyingPeriod();
$reflection = new \ReflectionClass($course);
$idProp = $reflection->getProperty('id');
$idProp->setValue($course, 456);
$document->setCourse($course);
$subjectConverterManager = $this->prophesize(SubjectConverterManagerInterface::class);
$subjectConverterManager->getSubjectsForEntity($course, true)->willReturn(
new SubjectBag($s = new Subject('accompanying_period', ['id' => 456]))
);
$converter = new AccompanyingCourseDocumentSubjectConverter();
$converter->setSubjectConverterManager($subjectConverterManager->reveal());
$subjectBag = $converter->convert($document, true);
$this->assertSame('accompanying_course_document', $subjectBag->subject->type);
$this->assertSame(['id' => 123], $subjectBag->subject->identifiers);
$this->assertCount(1, $subjectBag->associatedSubjects);
$this->assertSame($s, $subjectBag->associatedSubjects[0]);
}
}

View File

@@ -133,3 +133,8 @@ audit:
stored_object:
display: Document n°{id}
display_with_title: Document n°{id} - {title}
generic-doc:
list_for_accompanying_period: Liste des documents d'un parcours d'accompagnement
list_for_person: Liste des documents de l'usager
accompanying_course_document:
display: Document d'un parcours d'accompagnement n°{id}