Add audit functionality to HouseholdController, RelationApiController, and repository methods

- Integrated `TriggerAuditInterface` in `HouseholdController` and added audit triggers for view and update actions.
- Added onAfterFlush audit handling in `RelationApiController` to support create, update, and delete events.
- Updated `HouseholdMembersRepository` with a `find` method for retrieving `HouseholdMember` entities.
This commit is contained in:
2026-02-26 16:00:41 +01:00
parent b000255aea
commit 5eaf3ee828
5 changed files with 75 additions and 5 deletions

View File

@@ -11,7 +11,9 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Audit\TriggerAuditInterface;
use Chill\MainBundle\Entity\Address;
use Chill\MainBundle\Entity\AuditTrail;
use Chill\MainBundle\Form\Type\AddressDateType;
use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Form\HouseholdType;
@@ -27,12 +29,20 @@ use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Translation\TranslatableMessage;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Route(path: '/{_locale}/person/household')]
class HouseholdController extends AbstractController
{
public function __construct(private readonly TranslatorInterface $translator, private readonly PositionRepository $positionRepository, private readonly SerializerInterface $serializer, private readonly Security $security, private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry) {}
public function __construct(
private readonly TranslatorInterface $translator,
private readonly PositionRepository $positionRepository,
private readonly SerializerInterface $serializer,
private readonly Security $security,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly TriggerAuditInterface $triggerAudit,
) {}
/**
* @ParamConverter("household", options={"id": "household_id"})
@@ -40,6 +50,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/accompanying-period', name: 'chill_person_household_accompanying_period', methods: ['GET', 'HEAD'])]
public function accompanyingPeriod(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household, description: new TranslatableMessage('audit.household.list_accompanying_periods'));
$currentMembers = $household->getCurrentPersons();
$accompanyingPeriods = [];
@@ -90,6 +102,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/address/edit', name: 'chill_person_household_address_edit', methods: ['GET', 'HEAD', 'POST'])]
public function addressEdit(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household, description: new TranslatableMessage('audit.household.edit_address_page'));
// TODO ACL
$address_id = $request->query->get('address_id');
@@ -112,6 +126,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/addresses', name: 'chill_person_household_addresses', methods: ['GET', 'HEAD'])]
public function addresses(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household, description: new TranslatableMessage('audit.household.list_addresses'));
// TODO ACL
// TODO put these lines into a validator constraint on household->getAddress
@@ -138,6 +154,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/address/move', name: 'chill_person_household_address_move', methods: ['GET', 'HEAD', 'POST'])]
public function addressMove(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household, description: new TranslatableMessage('audit.household.address_move'));
// TODO ACL
return $this->render(
@@ -154,6 +172,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/address/edit_valid_from', name: 'chill_person_household_address_valid_from_edit', methods: ['GET', 'HEAD', 'POST'])]
public function addressValidFromEdit(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household);
$this->denyAccessUnlessGranted(HouseholdVoter::EDIT, $household);
if (!$request->query->has('address_id')) {
@@ -183,6 +203,8 @@ class HouseholdController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) {
$household->makeAddressConsistent();
($this->triggerAudit)(AuditTrail::AUDIT_UPDATE, $household, description: new TranslatableMessage('audit.household.address_valid_from_edit'));
$this->managerRegistry->getManager()->flush();
return $this->redirectToRoute('chill_person_household_addresses', [
@@ -206,12 +228,16 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/members/metadata/edit', name: 'chill_person_household_members_metadata_edit', methods: ['GET', 'POST'])]
public function editHouseholdMetadata(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household);
// TODO ACL
$form = $this->createMetadataForm($household);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
($this->triggerAudit)(AuditTrail::AUDIT_UPDATE, $household, description: new TranslatableMessage('audit.household.members_metadata_edit'));
$this->managerRegistry->getManager()->flush();
$this->addFlash('success', $this->translator->trans('household.data_saved'));
@@ -239,6 +265,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/relationship', name: 'chill_person_household_relationship', methods: ['GET', 'HEAD'])]
public function showRelationship(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household, description: new TranslatableMessage('audit.household.relationship'));
$jsonString = $this->serializer->serialize(
$household->getCurrentPersons(),
'json',
@@ -260,6 +288,8 @@ class HouseholdController extends AbstractController
#[Route(path: '/{household_id}/summary', name: 'chill_person_household_summary', methods: ['GET', 'HEAD'])]
public function summary(Request $request, Household $household)
{
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $household, description: new TranslatableMessage('audit.household.summary'));
// TODO ACL
$positions = $this->positionRepository

View File

@@ -19,7 +19,6 @@ use Chill\PersonBundle\Entity\Household\Household;
use Chill\PersonBundle\Entity\Household\HouseholdMember;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Form\CreationPersonType;
use Chill\PersonBundle\Privacy\PrivacyEvent;
use Chill\PersonBundle\Repository\PersonRepository;
use Chill\PersonBundle\Search\SimilarPersonMatcher;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
@@ -78,8 +77,6 @@ final class PersonController extends AbstractController
'You are not allowed to see this person.'
);
$event = new PrivacyEvent($person);
$this->eventDispatcher->dispatch($event, PrivacyEvent::PERSON_PRIVACY_EVENT);
($this->triggerAudit)(AuditTrail::AUDIT_VIEW, $person, description: new TranslatableMessage('audit.household.list_person_file'));
return $this->render(

View File

@@ -11,6 +11,29 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Controller;
use Chill\MainBundle\Audit\TriggerAuditInterface;
use Chill\MainBundle\CRUD\Controller\ApiController;
use Chill\MainBundle\Entity\AuditTrail;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\ConstraintViolationListInterface;
class RelationApiController extends ApiController {}
class RelationApiController extends ApiController
{
public function __construct(private TriggerAuditInterface $triggerAudit) {}
protected function onAfterFlush(string $action, Request $request, string $_format, $entity, ConstraintViolationListInterface $errors, array $more = []): ?Response
{
($this->triggerAudit)(
match ($action) {
'create' => AuditTrail::AUDIT_CREATE,
'update' => AuditTrail::AUDIT_UPDATE,
'delete' => AuditTrail::AUDIT_DELETE,
default => 'relation.action',
},
$entity,
);
return parent::onAfterFlush($action, $request, $_format, $entity, $errors, $more);
}
}

View File

@@ -23,4 +23,9 @@ final readonly class HouseholdMembersRepository
{
$this->repository = $entityManager->getRepository(HouseholdMember::class);
}
public function find($id): ?HouseholdMember
{
return $this->repository->find($id);
}
}

View File

@@ -1585,3 +1585,18 @@ audit:
person_resource:
list: Liste des personnes ressources
person_resource_number: "Personne ressource n°{id}: {name}"
household:
list_accompanying_periods: "Liste des parcours d'accompagnement du ménage"
edit_address_page: "Édition de l'adresse du ménage"
list_addresses: "Liste des adresses du ménage"
address_move: "Déménagement du ménage"
address_valid_from_edit: "Modification de la date de début de l'adresse du ménage"
members_metadata_edit: "Modification des métadonnées des membres du ménage"
relationship: "Relations du ménage"
summary: "Résumé du ménage"
list_person_file: Liste des ménages d'un usager
edit_participation: "Modification d'une participation au ménage"
household_not_found_with_id: 'Ménage non trouvé avec identifiant {id}'
household_member:
not_found_with_id: 'Membre du ménage non trouvé avec identifiant {id}'
member_in_household: 'membre du ménage'