From 32c5f2143889ed7ca6ec8589f0a626d43c7499b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 13 Feb 2026 16:56:04 +0100 Subject: [PATCH] Refactor subject conversion handling and enhance audit logging - Replaced `Subject|array` return types with `SubjectBag` in `SubjectConverterInterface` for more robust handling. - Updated `getSubjectsForEntity` to include an optional `$includeAssociated` parameter for finer control over associated subject resolution. - Refactored `AuditEvent` to differentiate `mainSubject` from associated subjects, improving clarity in audit logging. - Introduced database schema changes to add `main_subject` and `subjects` columns in the `chill_main_audit_trail` table. - Added `SubjectBag` class for grouped subject management and implemented deduplication logic. - Updated all converters and test cases to use the new `SubjectBag` approach, ensuring compatibility. - Improved event dispatching in controllers to utilize the updated `AuditEvent` structure and refined metadata handling. --- .../ChillMainBundle/Audit/AuditEvent.php | 1 + .../Audit/AuditEvent2Trail.php | 14 ++-- src/Bundle/ChillMainBundle/Audit/Subject.php | 22 ++++++ .../ChillMainBundle/Audit/SubjectBag.php | 39 ++++++++++ .../Audit/SubjectConverterInterface.php | 4 +- .../Audit/SubjectConverterManager.php | 6 +- .../SubjectConverterManagerInterface.php | 5 +- .../ChillMainBundle/Entity/AuditTrail.php | 30 ++++--- .../Repository/AuditTrailRepository.php | 20 ++++- .../Tests/Audit/AuditEvent2TrailTest.php | 28 ++++++- .../Tests/Audit/SubjectBagTest.php | 78 +++++++++++++++++++ .../Audit/SubjectConverterManagerTest.php | 30 ++----- .../Tests/Audit/SubjectTest.php | 74 ++++++++++++++++++ .../migrations/Version20260123162433.php | 15 +++- .../AccompanyingPeriodSubjectConverter.php | 9 ++- ...AccompanyingPeriodWorkSubjectConverter.php | 7 +- .../PersonSubjectConverter.php | 5 +- .../AccompanyingCourseController.php | 19 ++--- .../AccompanyingCourseWorkController.php | 8 +- .../Controller/PersonController.php | 2 +- 20 files changed, 332 insertions(+), 84 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Audit/SubjectBag.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Audit/SubjectBagTest.php create mode 100644 src/Bundle/ChillMainBundle/Tests/Audit/SubjectTest.php diff --git a/src/Bundle/ChillMainBundle/Audit/AuditEvent.php b/src/Bundle/ChillMainBundle/Audit/AuditEvent.php index e8ed86329..6cccd7e70 100644 --- a/src/Bundle/ChillMainBundle/Audit/AuditEvent.php +++ b/src/Bundle/ChillMainBundle/Audit/AuditEvent.php @@ -17,6 +17,7 @@ readonly class AuditEvent { public function __construct( public string $action, + public object $mainSubject, /** * @var list */ diff --git a/src/Bundle/ChillMainBundle/Audit/AuditEvent2Trail.php b/src/Bundle/ChillMainBundle/Audit/AuditEvent2Trail.php index 76bfe15e5..1930e939a 100644 --- a/src/Bundle/ChillMainBundle/Audit/AuditEvent2Trail.php +++ b/src/Bundle/ChillMainBundle/Audit/AuditEvent2Trail.php @@ -34,13 +34,10 @@ final readonly class AuditEvent2Trail implements AuditEvent2TrailInterface $event->description->trans($this->translator) : $event->description; - $subjects = array_reduce( - $event->subjects, - function (array $carry, mixed $item): array { - return array_merge($carry, $this->subjectConverterManager->getSubjectsForEntity($item)); - }, - [] - ); + $subjectBag = $this->subjectConverterManager->getSubjectsForEntity($event->mainSubject, true); + foreach ($event->subjects as $target) { + $subjectBag->append($this->subjectConverterManager->getSubjectsForEntity($target, false)); + } $user = $this->security->getUser(); @@ -50,7 +47,8 @@ final readonly class AuditEvent2Trail implements AuditEvent2TrailInterface $this->clock->now(), $user instanceof User ? $user : null, $description, - array_map(fn (Subject $subject) => $subject->asArray(), $subjects), + $subjectBag->subject->asArray(), + array_map(fn (Subject $subject) => $subject->asArray(), $subjectBag->associatedSubjects), $event->metadata, ); } diff --git a/src/Bundle/ChillMainBundle/Audit/Subject.php b/src/Bundle/ChillMainBundle/Audit/Subject.php index bcaf9db93..5501a9418 100644 --- a/src/Bundle/ChillMainBundle/Audit/Subject.php +++ b/src/Bundle/ChillMainBundle/Audit/Subject.php @@ -25,4 +25,26 @@ class Subject { return [...$this->identifiers, 't' => $this->type]; } + + public function isEqual(Subject $subject): bool + { + if ($subject->type !== $this->type) { + return false; + } + + foreach ($this->identifiers as $k => $identifierValue) { + if (!array_key_exists($k, $subject->identifiers)) { + return false; + } + if ($subject->identifiers[$k] !== $identifierValue) { + return false; + } + } + + if (count($this->identifiers) !== count($subject->identifiers)) { + return false; + } + + return true; + } } diff --git a/src/Bundle/ChillMainBundle/Audit/SubjectBag.php b/src/Bundle/ChillMainBundle/Audit/SubjectBag.php new file mode 100644 index 000000000..75db1cc80 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Audit/SubjectBag.php @@ -0,0 +1,39 @@ + + */ + public array $associatedSubjects = [], + ) {} + + public function append(SubjectBag $subjectBag): void + { + $toAppend = [$subjectBag->subject, ...$subjectBag->associatedSubjects]; + foreach ($toAppend as $subject) { + if ($subject->isEqual($this->subject)) { + continue; + } + foreach ($this->associatedSubjects as $associatedSubject) { + if ($subject->isEqual($associatedSubject)) { + continue 2; + } + } + $this->associatedSubjects[] = $subject; + } + } +} diff --git a/src/Bundle/ChillMainBundle/Audit/SubjectConverterInterface.php b/src/Bundle/ChillMainBundle/Audit/SubjectConverterInterface.php index b0ed59d7d..b7bc0091a 100644 --- a/src/Bundle/ChillMainBundle/Audit/SubjectConverterInterface.php +++ b/src/Bundle/ChillMainBundle/Audit/SubjectConverterInterface.php @@ -18,10 +18,8 @@ interface SubjectConverterInterface { /** * @param T $subject - * - * @return Subject|list */ - public function convert(mixed $subject): Subject|array; + public function convert(mixed $subject, bool $includeAssociated = false): SubjectBag; /** * Determines whether the given subject is supported for conversion. diff --git a/src/Bundle/ChillMainBundle/Audit/SubjectConverterManager.php b/src/Bundle/ChillMainBundle/Audit/SubjectConverterManager.php index a31aac42b..28116f727 100644 --- a/src/Bundle/ChillMainBundle/Audit/SubjectConverterManager.php +++ b/src/Bundle/ChillMainBundle/Audit/SubjectConverterManager.php @@ -22,7 +22,7 @@ final readonly class SubjectConverterManager implements SubjectConverterManagerI private iterable $converters, ) {} - public function getSubjectsForEntity(mixed $subject): array + public function getSubjectsForEntity(mixed $subject, bool $includeAssociated = false): SubjectBag { foreach ($this->converters as $converter) { if ($converter instanceof SubjectConverterManagerAwareInterface) { @@ -30,9 +30,7 @@ final readonly class SubjectConverterManager implements SubjectConverterManagerI } if ($converter->supportsConvert($subject)) { - $subjects = $converter->convert($subject); - - return $subjects instanceof Subject ? [$subjects] : $subjects; + return $converter->convert($subject); } } diff --git a/src/Bundle/ChillMainBundle/Audit/SubjectConverterManagerInterface.php b/src/Bundle/ChillMainBundle/Audit/SubjectConverterManagerInterface.php index eb09ba1b2..aebff51e8 100644 --- a/src/Bundle/ChillMainBundle/Audit/SubjectConverterManagerInterface.php +++ b/src/Bundle/ChillMainBundle/Audit/SubjectConverterManagerInterface.php @@ -13,8 +13,5 @@ namespace Chill\MainBundle\Audit; interface SubjectConverterManagerInterface { - /** - * @return list - */ - public function getSubjectsForEntity(mixed $subject): array; + public function getSubjectsForEntity(object $subject, bool $includeAssociated = false): SubjectBag; } diff --git a/src/Bundle/ChillMainBundle/Entity/AuditTrail.php b/src/Bundle/ChillMainBundle/Entity/AuditTrail.php index b81bba77d..3778ff029 100644 --- a/src/Bundle/ChillMainBundle/Entity/AuditTrail.php +++ b/src/Bundle/ChillMainBundle/Entity/AuditTrail.php @@ -26,26 +26,28 @@ class AuditTrail public function __construct( #[ORM\Id] - #[ORM\Column(type: 'uuid', unique: true, name: 'id')] + #[ORM\Column(name: 'id', type: 'uuid', unique: true)] private UuidInterface $id, - #[ORM\Column(type: Types::STRING, length: 255, name: 'action')] + #[ORM\Column(name: 'action', type: Types::STRING, length: 255)] private string $action, - #[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE, name: 'occurredat')] + #[ORM\Column(name: 'occurredat', type: Types::DATETIMETZ_IMMUTABLE)] private \DateTimeImmutable $occurredAt, #[ORM\ManyToOne(targetEntity: User::class)] - #[ORM\JoinColumn(nullable: true, name: 'user_id', referencedColumnName: 'id')] + #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)] private ?User $user = null, - #[ORM\Column(type: Types::STRING, length: 255, nullable: false, options: ['default' => ''], name: 'description')] + #[ORM\Column(name: 'description', type: Types::STRING, nullable: false, options: ['default' => ''])] private string $description = '', + #[ORM\Column(name: 'main_subject', type: Types::JSON, options: ['jsonb' => true, 'default' => "'[]'::jsonb"])] + private array $mainSubject = [], /** * @var list> */ - #[ORM\Column(type: Types::JSON, options: ['jsonb' => true, 'default' => "'[]'::jsonb"], name: 'targets')] - private array $targets = [], + #[ORM\Column(name: 'subjects', type: Types::JSON, options: ['jsonb' => true, 'default' => "'[]'::jsonb"])] + private array $subjects = [], /** * @var array */ - #[ORM\Column(type: Types::JSON, options: ['jsonb' => true, 'default' => "'[]'::jsonb"], name: 'metadata')] + #[ORM\Column(name: 'metadata', type: Types::JSON, options: ['jsonb' => true, 'default' => "'[]'::jsonb"])] private array $metadata = [], ) {} @@ -77,9 +79,17 @@ class AuditTrail /** * @return list> */ - public function getTargets(): array + public function getSubjects(): array { - return $this->targets; + return $this->subjects; + } + + /** + * @return array + */ + public function getMainSubject(): array + { + return $this->mainSubject; } /** diff --git a/src/Bundle/ChillMainBundle/Repository/AuditTrailRepository.php b/src/Bundle/ChillMainBundle/Repository/AuditTrailRepository.php index e9030da31..fc6b9206e 100644 --- a/src/Bundle/ChillMainBundle/Repository/AuditTrailRepository.php +++ b/src/Bundle/ChillMainBundle/Repository/AuditTrailRepository.php @@ -42,7 +42,8 @@ class AuditTrailRepository extends ServiceEntityRepository 'occurredat' => ':occured_at', 'user_id' => ':user_id', 'description' => ':description', - 'targets' => ':targets', + 'main_subject' => ':main_subject', + 'subjects' => ':subjects', 'metadata' => ':metadata', ]) ->setParameter('id', $auditTrail->getId()) @@ -50,9 +51,24 @@ class AuditTrailRepository extends ServiceEntityRepository ->setParameter('occured_at', $auditTrail->getOccurredAt(), Types::DATETIMETZ_IMMUTABLE) ->setParameter('user_id', $auditTrail->getUser()?->getId(), Types::INTEGER) ->setParameter('description', $auditTrail->getDescription()) - ->setParameter('targets', $auditTrail->getTargets(), Types::JSON) + ->setParameter('main_subject', $auditTrail->getMainSubject(), Types::JSON) + ->setParameter('subjects', $auditTrail->getSubjects(), Types::JSON) ->setParameter('metadata', $auditTrail->getMetadata(), Types::JSON); $sql->executeQuery(); } + + /** + * @param array{} $criteria + * + * @return array + */ + public function findByCriteria(array $criteria, int $offset = 0, int $limit = 100): array + { + return $this->createQueryBuilder('audit') + ->orderBy('audit.occurredAt', 'DESC') + ->setMaxResults($limit) + ->setFirstResult($offset) + ->getQuery()->getResult(); + } } diff --git a/src/Bundle/ChillMainBundle/Tests/Audit/AuditEvent2TrailTest.php b/src/Bundle/ChillMainBundle/Tests/Audit/AuditEvent2TrailTest.php index 0f0add5ce..303fd54a9 100644 --- a/src/Bundle/ChillMainBundle/Tests/Audit/AuditEvent2TrailTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Audit/AuditEvent2TrailTest.php @@ -14,6 +14,7 @@ namespace Chill\MainBundle\Tests\Audit; use Chill\MainBundle\Audit\AuditEvent; use Chill\MainBundle\Audit\AuditEvent2Trail; use Chill\MainBundle\Audit\Subject; +use Chill\MainBundle\Audit\SubjectBag; use Chill\MainBundle\Audit\SubjectConverterManagerInterface; use Chill\MainBundle\Entity\AuditTrail; use PHPUnit\Framework\TestCase; @@ -44,15 +45,22 @@ class AuditEvent2TrailTest extends TestCase $event = new AuditEvent( action: 'test_action', - subjects: [$subject = new \stdClass()], + mainSubject: $subject = new \stdClass(), description: $description->reveal(), metadata: ['foo' => 'bar'] ); $security->getUser()->willReturn(null); - $subjectConverterManager->getSubjectsForEntity($subject) - ->willReturn([new Subject('stdClass', ['id' => '1'])]); + $subjectConverterManager->getSubjectsForEntity($subject, true) + ->willReturn( + new SubjectBag( + new Subject('stdClass', ['id' => '1']), + [ + new Subject('stdClass2', ['id' => '2']), + ] + ) + ); $service = new AuditEvent2Trail( $translator->reveal(), @@ -66,7 +74,8 @@ class AuditEvent2TrailTest extends TestCase $this->assertInstanceOf(AuditTrail::class, $trail); $this->assertSame('test_action', $trail->getAction()); $this->assertSame('translated description', $trail->getDescription()); - $this->assertSame([['id' => '1', 't' => 'stdClass']], $trail->getTargets()); + $this->assertSame(['id' => '1', 't' => 'stdClass'], $trail->getMainSubject()); + $this->assertSame([['id' => '2', 't' => 'stdClass2']], $trail->getSubjects()); $this->assertSame(['foo' => 'bar'], $trail->getMetadata()); $this->assertEquals($clock->now(), $trail->getOccurredAt()); } @@ -80,11 +89,22 @@ class AuditEvent2TrailTest extends TestCase $event = new AuditEvent( action: 'test_action', + mainSubject: $subject = new \stdClass(), subjects: [], description: 'string description', metadata: [] ); + $subjectConverterManager->getSubjectsForEntity($subject, true) + ->willReturn( + new SubjectBag( + new Subject('stdClass', ['id' => '1']), + [ + new Subject('stdClass2', ['id' => 2]), + ] + ) + ); + $security->getUser()->willReturn(null); $service = new AuditEvent2Trail( diff --git a/src/Bundle/ChillMainBundle/Tests/Audit/SubjectBagTest.php b/src/Bundle/ChillMainBundle/Tests/Audit/SubjectBagTest.php new file mode 100644 index 000000000..272b36629 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Audit/SubjectBagTest.php @@ -0,0 +1,78 @@ + '1']); + $bag = new SubjectBag($subject1); + + $subject2 = new Subject('type2', ['id' => '2']); + $associated1 = new Subject('type3', ['id' => '3']); + $bag2 = new SubjectBag($subject2, [$associated1]); + + $bag->append($bag2); + + $this->assertCount(2, $bag->associatedSubjects); + $this->assertSame($subject2, $bag->associatedSubjects[0]); + $this->assertSame($associated1, $bag->associatedSubjects[1]); + } + + public function testAppendAvoidDuplicates(): void + { + $subject1 = new Subject('type1', ['id' => '1']); + $associated1 = new Subject('type2', ['id' => '2']); + $bag = new SubjectBag($subject1, [$associated1]); + + // Duplicate of associated1 + $subject2 = new Subject('type2', ['id' => '2']); + $bag2 = new SubjectBag($subject2); + + $bag->append($bag2); + + $this->assertCount(1, $bag->associatedSubjects, 'Should not add duplicate of already present associated subject'); + + // Add another one + $subject3 = new Subject('type3', ['id' => '3']); + $bag3 = new SubjectBag($subject3, [$subject2]); // subject2 is duplicate of associated1 + + $bag->append($bag3); + $this->assertCount(2, $bag->associatedSubjects); + $this->assertSame($subject3, $bag->associatedSubjects[1]); + } + + public function testAppendAvoidDuplicateWithMainSubject(): void + { + $subject1 = new Subject('type1', ['id' => '1']); + $associated1 = new Subject('type2', ['id' => '2']); + $bag = new SubjectBag($subject1, [$associated1]); + + // Duplicate of associated1 + $subject2 = new Subject('type1', ['id' => '1']); + $bag2 = new SubjectBag($subject2); + + $bag->append($bag2); + + $this->assertCount(1, $bag->associatedSubjects, 'Should not add duplicate of already present associated subject'); + } +} diff --git a/src/Bundle/ChillMainBundle/Tests/Audit/SubjectConverterManagerTest.php b/src/Bundle/ChillMainBundle/Tests/Audit/SubjectConverterManagerTest.php index 39bbacede..558dbab08 100644 --- a/src/Bundle/ChillMainBundle/Tests/Audit/SubjectConverterManagerTest.php +++ b/src/Bundle/ChillMainBundle/Tests/Audit/SubjectConverterManagerTest.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Tests\Audit; use Chill\MainBundle\Audit\Exception\ConvertSubjectException; use Chill\MainBundle\Audit\Subject; +use Chill\MainBundle\Audit\SubjectBag; use Chill\MainBundle\Audit\SubjectConverterInterface; use Chill\MainBundle\Audit\SubjectConverterManager; use PHPUnit\Framework\TestCase; @@ -38,7 +39,7 @@ class SubjectConverterManagerTest extends TestCase public function testReturnsSingleSubjectWhenConverterReturnsSubject(): void { $subject = new \stdClass(); - $auditSubject = new Subject('type', ['id' => '123']); + $auditSubject = new SubjectBag(new Subject('type', ['id' => '123'])); $converter = $this->prophesize(SubjectConverterInterface::class); $converter->supportsConvert($subject)->willReturn(true); @@ -48,33 +49,13 @@ class SubjectConverterManagerTest extends TestCase $result = $manager->getSubjectsForEntity($subject); - $this->assertCount(1, $result); - $this->assertSame(['id' => '123', 't' => 'type'], $result[0]->asArray()); - } - - public function testReturnsListOfSubjectsWhenConverterReturnsArray(): void - { - $subject = new \stdClass(); - $auditSubject1 = new Subject('type1', ['id' => '123']); - $auditSubject2 = new Subject('type2', ['id' => '456']); - - $converter = $this->prophesize(SubjectConverterInterface::class); - $converter->supportsConvert($subject)->willReturn(true); - $converter->convert($subject)->willReturn([$auditSubject1, $auditSubject2]); - - $manager = new SubjectConverterManager([$converter->reveal()]); - - $result = $manager->getSubjectsForEntity($subject); - - $this->assertCount(2, $result); - $this->assertSame(['id' => '123', 't' => 'type1'], $result[0]->asArray()); - $this->assertSame(['id' => '456', 't' => 'type2'], $result[1]->asArray()); + $this->assertSame($auditSubject, $result); } public function testSkipsConverterThatDoesNotSupportSubject(): void { $subject = new \stdClass(); - $auditSubject = new Subject('type', ['id' => '123']); + $auditSubject = new SubjectBag(new Subject('type', ['id' => '123'])); $converter1 = $this->prophesize(SubjectConverterInterface::class); $converter1->supportsConvert($subject)->willReturn(false); @@ -88,7 +69,6 @@ class SubjectConverterManagerTest extends TestCase $result = $manager->getSubjectsForEntity($subject); - $this->assertCount(1, $result); - $this->assertSame(['id' => '123', 't' => 'type'], $result[0]->asArray()); + $this->assertSame($auditSubject, $result); } } diff --git a/src/Bundle/ChillMainBundle/Tests/Audit/SubjectTest.php b/src/Bundle/ChillMainBundle/Tests/Audit/SubjectTest.php new file mode 100644 index 000000000..cde157c77 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Audit/SubjectTest.php @@ -0,0 +1,74 @@ + '1', 'name' => 'test']); + $subject2 = new Subject('type1', ['id' => '1', 'name' => 'test']); + + $this->assertTrue($subject1->isEqual($subject2)); + } + + public function testIsEqualDifferentType(): void + { + $subject1 = new Subject('type1', ['id' => '1']); + $subject2 = new Subject('type2', ['id' => '1']); + + $this->assertFalse($subject1->isEqual($subject2)); + } + + public function testIsEqualDifferentIdentifierValue(): void + { + $subject1 = new Subject('type1', ['id' => '1']); + $subject2 = new Subject('type1', ['id' => '2']); + + $this->assertFalse($subject1->isEqual($subject2)); + } + + public function testIsEqualDifferentIdentifierKey(): void + { + $subject1 = new Subject('type1', ['id' => '1']); + $subject2 = new Subject('type1', ['other' => '1']); + + $this->assertFalse($subject1->isEqual($subject2)); + } + + public function testIsEqualDifferentIdentifierCount(): void + { + $subject1 = new Subject('type1', ['id' => '1', 'name' => 'test']); + $subject2 = new Subject('type1', ['id' => '1']); + + $this->assertFalse($subject1->isEqual($subject2)); + + $subject3 = new Subject('type1', ['id' => '1']); + $subject4 = new Subject('type1', ['id' => '1', 'name' => 'test']); + + $this->assertFalse($subject3->isEqual($subject4)); + } + + public function testAsArray(): void + { + $subject = new Subject('type1', ['id' => '1', 'name' => 'test']); + $this->assertSame(['id' => '1', 'name' => 'test', 't' => 'type1'], $subject->asArray()); + } +} diff --git a/src/Bundle/ChillMainBundle/migrations/Version20260123162433.php b/src/Bundle/ChillMainBundle/migrations/Version20260123162433.php index d49474615..248b3f75b 100644 --- a/src/Bundle/ChillMainBundle/migrations/Version20260123162433.php +++ b/src/Bundle/ChillMainBundle/migrations/Version20260123162433.php @@ -23,7 +23,20 @@ final class Version20260123162433 extends AbstractMigration public function up(Schema $schema): void { - $this->addSql("CREATE TABLE chill_main_audit_trail (id UUID NOT NULL, user_id INT DEFAULT NULL, action VARCHAR(255) NOT NULL, occurredAt TIMESTAMP(0) WITH TIME ZONE NOT NULL, description VARCHAR(255) DEFAULT '' NOT NULL, targets JSONB DEFAULT '[]'::jsonb NOT NULL, metadata JSONB DEFAULT '[]'::jsonb NOT NULL, PRIMARY KEY(id))"); + $this->addSql( + <<<'SQL' + CREATE TABLE chill_main_audit_trail ( + id UUID NOT NULL, + user_id INT DEFAULT NULL, + action VARCHAR(255) NOT NULL, + occurredAt TIMESTAMP(0) WITH TIME ZONE NOT NULL, + description TEXT DEFAULT '' NOT NULL, + main_subject JSONB DEFAULT '[]'::jsonb NOT NULL, + subjects JSONB DEFAULT '[]'::jsonb NOT NULL, + metadata JSONB DEFAULT '[]'::jsonb NOT NULL, + PRIMARY KEY(id)) + SQL + ); $this->addSql('CREATE INDEX IDX_331A47F4A76ED395 ON chill_main_audit_trail (user_id)'); $this->addSql('COMMENT ON COLUMN chill_main_audit_trail.id IS \'(DC2Type:uuid)\''); $this->addSql('COMMENT ON COLUMN chill_main_audit_trail.occurredAt IS \'(DC2Type:datetimetz_immutable)\''); diff --git a/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodSubjectConverter.php b/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodSubjectConverter.php index 6625ecd41..e5da9b9fd 100644 --- a/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodSubjectConverter.php +++ b/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodSubjectConverter.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Audit\SubjectConverter; 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; @@ -24,15 +25,15 @@ class AccompanyingPeriodSubjectConverter implements SubjectConverterInterface, S { use SubjectConverterManagerAwareTrait; - public function convert(mixed $subject): Subject|array + public function convert(mixed $subject, bool $includeAssociated = false): SubjectBag { - $data = [new Subject('accompanying_period', ['id' => $subject->getId()])]; + $main = new SubjectBag(new Subject('accompanying_period', ['id' => $subject->getId()])); foreach ($subject->getCurrentParticipations() as $participation) { - array_push($data, ...$this->subjectConverterManager->getSubjectsForEntity($participation->getPerson())); + $main->append($this->subjectConverterManager->getSubjectsForEntity($participation->getPerson(), false)); } - return $data; + return $main; } public function supportsConvert(mixed $subject): bool diff --git a/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodWorkSubjectConverter.php b/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodWorkSubjectConverter.php index 4e5602de7..0648f5f81 100644 --- a/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodWorkSubjectConverter.php +++ b/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/AccompanyingPeriodWorkSubjectConverter.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Audit\SubjectConverter; 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; @@ -24,12 +25,12 @@ class AccompanyingPeriodWorkSubjectConverter implements SubjectConverterInterfac { use SubjectConverterManagerAwareTrait; - public function convert(mixed $subject): Subject|array + public function convert(mixed $subject, bool $includeAssociated = false): SubjectBag { - $data = [new Subject('accompanying_period_work', ['id' => $subject->getId()])]; + $data = new SubjectBag(new Subject('accompanying_period_work', ['id' => $subject->getId()])); foreach ($subject->getPersons() as $person) { - array_push($data, ...$this->subjectConverterManager->getSubjectsForEntity($person)); + $data->append($this->subjectConverterManager->getSubjectsForEntity($person)); } return $data; diff --git a/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/PersonSubjectConverter.php b/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/PersonSubjectConverter.php index e33b18277..327acd58b 100644 --- a/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/PersonSubjectConverter.php +++ b/src/Bundle/ChillPersonBundle/Audit/SubjectConverter/PersonSubjectConverter.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\PersonBundle\Audit\SubjectConverter; use Chill\MainBundle\Audit\Subject; +use Chill\MainBundle\Audit\SubjectBag; use Chill\MainBundle\Audit\SubjectConverterInterface; use Chill\PersonBundle\Entity\Person; @@ -20,9 +21,9 @@ use Chill\PersonBundle\Entity\Person; */ class PersonSubjectConverter implements SubjectConverterInterface { - public function convert(mixed $subject): Subject|array + public function convert(mixed $subject, bool $includeAssociated = false): SubjectBag { - return new Subject('person', ['id' => $subject->getId()]); + return new SubjectBag(new Subject('person', ['id' => $subject->getId()])); } public function supportsConvert(mixed $subject): bool diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php index 57432e280..a8d83a0dc 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php @@ -80,7 +80,8 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle $this->eventDispatcher->dispatch( new AuditEvent( AuditTrail::AUDIT_UPDATE, - [$accompanyingCourse], + $accompanyingCourse, + [], new TranslatableMessage('accompanying_period.audit.close'), ['action' => 'close'] ) @@ -133,7 +134,7 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle $em->remove($accompanyingCourse); $em->flush(); - $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_DELETE, [$accompanyingCourse])); + $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_DELETE, $accompanyingCourse)); $this->addFlash('success', $this->translator ->trans('The accompanying course has been successfully removed.')); @@ -175,9 +176,9 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle $this->eventDispatcher->dispatch(new AuditEvent( AuditTrail::AUDIT_VIEW, - [$accompanyingCourse], - new TranslatableMessage('accompanying_period.audit.show_edit_page'), - ['action' => 'show_edit_page'] + $accompanyingCourse, + description: new TranslatableMessage('accompanying_period.audit.show_edit_page'), + metadata: ['action' => 'show_edit_page'] )); return $this->render('@ChillPerson/AccompanyingCourse/edit.html.twig', [ @@ -212,7 +213,7 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle { $this->denyAccessUnlessGranted(AccompanyingPeriodVoter::SEE, $accompanyingCourse); - $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_VIEW, [$accompanyingCourse])); + $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_VIEW, $accompanyingCourse)); // compute some warnings // get persons without household @@ -283,7 +284,7 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle $em->persist($period); $em->flush(); - $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_CREATE, [$period])); + $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_CREATE, $period)); return $this->redirectToRoute('chill_person_accompanying_course_edit', [ 'accompanying_period_id' => $period->getId(), @@ -322,7 +323,7 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle $em->persist($period); $em->flush(); - $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_CREATE, [$period])); + $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_CREATE, $period)); return $this->redirectToRoute('chill_person_accompanying_course_edit', [ 'accompanying_period_id' => $period->getId(), @@ -348,7 +349,7 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle $accompanyingCourse->reOpen(); $this->managerRegistry->getManager()->flush(); - $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_UPDATE, [$accompanyingCourse], new TranslatableMessage('accompanying_period.audit.reopen'), ['action' => 'reopen'])); + $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_UPDATE, $accompanyingCourse, description: new TranslatableMessage('accompanying_period.audit.reopen'), metadata: ['action' => 'reopen'])); return $this->redirectToRoute('chill_person_accompanying_course_index', [ 'accompanying_period_id' => $accompanyingCourse->getId(), diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index a2c9dae82..c7d5ecbf9 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -162,9 +162,9 @@ final class AccompanyingCourseWorkController extends AbstractController $this->eventDispatcher->dispatch( new AuditEvent( AuditTrail::AUDIT_VIEW, - [$period], - new TranslatableMessage('accompanying_period.audit.show_list_work'), - ['action' => 'show_list_work'] + $period, + description: new TranslatableMessage('accompanying_period.audit.show_list_work'), + metadata: ['action' => 'show_list_work'] ) ); @@ -188,7 +188,7 @@ final class AccompanyingCourseWorkController extends AbstractController $this->eventDispatcher->dispatch( new AuditEvent( AuditTrail::AUDIT_VIEW, - [$work], + $work, ) ); diff --git a/src/Bundle/ChillPersonBundle/Controller/PersonController.php b/src/Bundle/ChillPersonBundle/Controller/PersonController.php index 97d568f58..2e673daf5 100644 --- a/src/Bundle/ChillPersonBundle/Controller/PersonController.php +++ b/src/Bundle/ChillPersonBundle/Controller/PersonController.php @@ -206,7 +206,7 @@ final class PersonController extends AbstractController 'You are not allowed to see this person.' ); - $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_VIEW, [$person])); + $this->eventDispatcher->dispatch(new AuditEvent(AuditTrail::AUDIT_VIEW, $person)); return $this->render( '@ChillPerson/Person/view.html.twig',