Introduce PersonsUpdateEvent for handling person modifications in tickets

- Added `PersonsUpdateEvent`, `TicketUpdateEvent`, and `TicketUpdateKindEnum` to encapsulate and differentiate ticket update types.
- Updated `SetPersonsCommandHandler` to dispatch `PersonsUpdateEvent` when persons are added or removed.
- Extended tests to verify event dispatching for person modifications.
This commit is contained in:
2025-10-15 15:05:49 +02:00
parent 4765d4fe28
commit e09676e88b
5 changed files with 141 additions and 6 deletions

View File

@@ -15,9 +15,12 @@ use Chill\MainBundle\Entity\User;
use Chill\TicketBundle\Action\Ticket\SetPersonsCommand;
use Chill\TicketBundle\Entity\PersonHistory;
use Chill\TicketBundle\Entity\Ticket;
use Chill\TicketBundle\Event\PersonsUpdateEvent;
use Chill\TicketBundle\Event\TicketUpdateEvent;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Clock\ClockInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
final readonly class SetPersonsCommandHandler
{
@@ -25,10 +28,13 @@ final readonly class SetPersonsCommandHandler
private ClockInterface $clock,
private EntityManagerInterface $entityManager,
private Security $security,
private EventDispatcherInterface $eventDispatcher,
) {}
public function handle(Ticket $ticket, SetPersonsCommand $command): void
{
$event = new PersonsUpdateEvent($ticket);
// remove existing addresses which are not in the new addresses
foreach ($ticket->getPersonHistories() as $personHistory) {
if (null !== $personHistory->getEndDate()) {
@@ -40,6 +46,7 @@ final readonly class SetPersonsCommandHandler
if (($user = $this->security->getUser()) instanceof User) {
$personHistory->setRemovedBy($user);
}
$event->personsRemoved[] = $personHistory->getPerson();
}
}
@@ -51,6 +58,11 @@ final readonly class SetPersonsCommandHandler
$history = new PersonHistory($person, $ticket, $this->clock->now());
$this->entityManager->persist($history);
$event->personsAdded[] = $person;
}
if ($event->hasChanges()) {
$this->eventDispatcher->dispatch($event, TicketUpdateEvent::class);
}
}
}

View File

@@ -0,0 +1,38 @@
<?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\TicketBundle\Event;
use Chill\PersonBundle\Entity\Person;
use Chill\TicketBundle\Entity\Ticket;
class PersonsUpdateEvent extends TicketUpdateEvent
{
public function __construct(Ticket $ticket)
{
parent::__construct(TicketUpdateKindEnum::UPDATE_PERSONS, $ticket);
}
/**
* @var list<Person>
*/
public $personsAdded = [];
/**
* @var list<Person>
*/
public $personsRemoved = [];
public function hasChanges(): bool
{
return count($this->personsAdded) > 0 || count($this->personsRemoved) > 0;
}
}

View File

@@ -0,0 +1,22 @@
<?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\TicketBundle\Event;
use Chill\TicketBundle\Entity\Ticket;
abstract class TicketUpdateEvent
{
public function __construct(
public readonly TicketUpdateKindEnum $updateKind,
public readonly Ticket $ticket,
) {}
}

View File

@@ -0,0 +1,23 @@
<?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\TicketBundle\Event;
enum TicketUpdateKindEnum: string
{
case UPDATE_ADDRESSEE = 'UPDATE_ADDRESSEE';
case ADD_COMMENT = 'ADD_COMMENT';
case TOGGLE_EMERGENCY = 'TOGGLE_EMERGENCY';
case TOGGLE_STATE = 'TOGGLE_STATE';
case UPDATE_MOTIVE = 'UPDATE_MOTIVE';
case UPDATE_CALLER = 'UPDATE_CALLER';
case UPDATE_PERSONS = 'UPDATE_PERSONS';
}

View File

@@ -17,12 +17,15 @@ use Chill\TicketBundle\Action\Ticket\Handler\SetPersonsCommandHandler;
use Chill\TicketBundle\Action\Ticket\SetPersonsCommand;
use Chill\TicketBundle\Entity\PersonHistory;
use Chill\TicketBundle\Entity\Ticket;
use Chill\TicketBundle\Event\PersonsUpdateEvent;
use Chill\TicketBundle\Event\TicketUpdateEvent;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\Clock\MockClock;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
/**
* @internal
@@ -42,7 +45,16 @@ final class SetPersonsCommandHandlerTest extends TestCase
$entityManager->persist(Argument::that(fn ($arg) => $arg instanceof PersonHistory && $arg->getPerson() === $person1))->shouldBeCalledOnce();
$entityManager->persist(Argument::that(fn ($arg) => $arg instanceof PersonHistory && $arg->getPerson() === $group1))->shouldBeCalledOnce();
$handler = $this->buildHandler($entityManager->reveal());
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
$eventDispatcher->dispatch(
Argument::that(fn ($arg) => $arg instanceof PersonsUpdateEvent
&& in_array($person1, $arg->personsAdded, true)
&& in_array($group1, $arg->personsAdded, true)
&& [] === $arg->personsRemoved),
TicketUpdateEvent::class
)->shouldBeCalled();
$handler = $this->buildHandler($entityManager->reveal(), $eventDispatcher->reveal());
$handler->handle($ticket, $command);
@@ -59,7 +71,15 @@ final class SetPersonsCommandHandlerTest extends TestCase
$entityManager = $this->prophesize(EntityManagerInterface::class);
$entityManager->persist(Argument::that(fn ($arg) => $arg instanceof PersonHistory && $arg->getPerson() === $person))->shouldNotBeCalled();
$handler = $this->buildHandler($entityManager->reveal());
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
$eventDispatcher->dispatch(
Argument::that(
fn ($arg) => $arg instanceof PersonsUpdateEvent
),
TicketUpdateEvent::class
)->shouldNotBeCalled();
$handler = $this->buildHandler($entityManager->reveal(), $eventDispatcher->reveal());
$handler->handle($ticket, $command);
@@ -78,7 +98,17 @@ final class SetPersonsCommandHandlerTest extends TestCase
$entityManager = $this->prophesize(EntityManagerInterface::class);
$entityManager->persist(Argument::that(fn ($arg) => $arg instanceof PersonHistory && $arg->getPerson() === $person2))->shouldBeCalled();
$handler = $this->buildHandler($entityManager->reveal());
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
$eventDispatcher->dispatch(
Argument::that(
fn ($arg) => $arg instanceof PersonsUpdateEvent
&& in_array($person, $arg->personsRemoved, true) && 1 === count($arg->personsRemoved)
&& in_array($person2, $arg->personsAdded, true) && 1 === count($arg->personsAdded)
),
TicketUpdateEvent::class
)->shouldBeCalled();
$handler = $this->buildHandler($entityManager->reveal(), $eventDispatcher->reveal());
$handler->handle($ticket, $command);
@@ -95,18 +125,28 @@ final class SetPersonsCommandHandlerTest extends TestCase
$entityManager = $this->prophesize(EntityManagerInterface::class);
$entityManager->persist(Argument::that(fn ($arg) => $arg instanceof PersonHistory && $arg->getPerson() === $person))->shouldBeCalledOnce();
$handler = $this->buildHandler($entityManager->reveal());
$eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
$eventDispatcher->dispatch(
Argument::that(
fn ($arg) => $arg instanceof PersonsUpdateEvent
&& in_array($person, $arg->personsAdded, true) && 1 === count($arg->personsAdded)
&& [] === $arg->personsRemoved
),
TicketUpdateEvent::class
)->shouldBeCalled();
$handler = $this->buildHandler($entityManager->reveal(), $eventDispatcher->reveal());
$handler->handle($ticket, $command);
self::assertCount(1, $ticket->getPersons());
}
private function buildHandler(EntityManagerInterface $entityManager): SetPersonsCommandHandler
private function buildHandler(EntityManagerInterface $entityManager, EventDispatcherInterface $eventDispatcher): SetPersonsCommandHandler
{
$security = $this->prophesize(Security::class);
$security->getUser()->willReturn(new User());
return new SetPersonsCommandHandler(new MockClock(), $entityManager, $security->reveal());
return new SetPersonsCommandHandler(new MockClock(), $entityManager, $security->reveal(), $eventDispatcher);
}
}