mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-12 21:34:25 +00:00
Manage the persons' assocation with ticket through SetPersonsCommand and dedicated handler
This commit is contained in:
parent
a777588bb8
commit
631f047338
@ -0,0 +1,56 @@
|
||||
<?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\Action\Ticket\Handler;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\TicketBundle\Action\Ticket\SetPersonsCommand;
|
||||
use Chill\TicketBundle\Entity\PersonHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
final readonly class SetPersonsCommandHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ClockInterface $clock,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
public function handle(Ticket $ticket, SetPersonsCommand $command): void
|
||||
{
|
||||
// remove existing addresses which are not in the new addresses
|
||||
foreach ($ticket->getPersonHistories() as $personHistory) {
|
||||
if (null !== $personHistory->getEndDate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_array($personHistory->getPerson(), $command->persons, true)) {
|
||||
$personHistory->setEndDate($this->clock->now());
|
||||
if (($user = $this->security->getUser()) instanceof User) {
|
||||
$personHistory->setRemovedBy($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add new addresses
|
||||
foreach ($command->persons as $person) {
|
||||
if (in_array($person, $ticket->getPersons(), true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$history = new PersonHistory($person, $ticket, $this->clock->now());
|
||||
$this->entityManager->persist($history);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?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\Action\Ticket;
|
||||
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Validator\Constraints\GreaterThan;
|
||||
|
||||
class SetPersonsCommand
|
||||
{
|
||||
public function __construct(
|
||||
/**
|
||||
* @var list<Person>
|
||||
*/
|
||||
#[GreaterThan(0)]
|
||||
#[Groups(['read'])]
|
||||
public array $persons
|
||||
) {}
|
||||
}
|
@ -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\TicketBundle\Controller;
|
||||
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\SetPersonsCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\SetPersonsCommand;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
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\Validator\Validator\ValidatorInterface;
|
||||
|
||||
final readonly class SetPersonsController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private EntityManagerInterface $entityManager,
|
||||
private SerializerInterface $serializer,
|
||||
private SetPersonsCommandHandler $setPersonsCommandHandler,
|
||||
private ValidatorInterface $validator,
|
||||
) {}
|
||||
|
||||
#[Route('/api/1.0/ticket/{id}/persons/set', methods: ['POST'])]
|
||||
public function setPersons(Ticket $ticket, Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('Only users can set addressees.');
|
||||
}
|
||||
|
||||
$command = $this->serializer->deserialize($request->getContent(), SetPersonsCommand::class, 'json', [AbstractNormalizer::GROUPS => ['read']]);
|
||||
|
||||
return $this->registerSetPersons($command, $ticket);
|
||||
}
|
||||
|
||||
private function registerSetPersons(SetPersonsCommand $command, Ticket $ticket): Response
|
||||
{
|
||||
if (0 < count($errors = $this->validator->validate($command))) {
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($errors, 'json'),
|
||||
Response::HTTP_UNPROCESSABLE_ENTITY,
|
||||
[],
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
$this->setPersonsCommandHandler->handle($ticket, $command);
|
||||
|
||||
$this->entityManager->flush();
|
||||
|
||||
return new JsonResponse(
|
||||
$this->serializer->serialize($ticket, 'json', ['groups' => 'read']),
|
||||
Response::HTTP_OK,
|
||||
[],
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
@ -91,4 +91,9 @@ class PersonHistory implements TrackCreationInterface
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setRemovedBy(?User $removedBy): void
|
||||
{
|
||||
$this->removedBy = $removedBy;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,122 @@
|
||||
<?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\Tests\Action\Ticket\Handler;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Chill\TicketBundle\Action\Ticket\Handler\SetPersonsCommandHandler;
|
||||
use Chill\TicketBundle\Action\Ticket\SetPersonsCommand;
|
||||
use Chill\TicketBundle\Entity\PersonHistory;
|
||||
use Chill\TicketBundle\Entity\Ticket;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
final class SetPersonsCommandHandlerTest extends TestCase
|
||||
{
|
||||
use ProphecyTrait;
|
||||
|
||||
public function testHandleOnEmptyAddresses(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$command = new SetPersonsCommand([$person1 = new Person(), $group1 = new Person()]);
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->persist(Argument::that(function ($arg) use ($person1) {
|
||||
return $arg instanceof PersonHistory && $arg->getPerson() === $person1;
|
||||
}))->shouldBeCalledOnce();
|
||||
$entityManager->persist(Argument::that(function ($arg) use ($group1) {
|
||||
return $arg instanceof PersonHistory && $arg->getPerson() === $group1;
|
||||
}))->shouldBeCalledOnce();
|
||||
|
||||
$handler = $this->buildHandler($entityManager->reveal());
|
||||
|
||||
$handler->handle($ticket, $command);
|
||||
|
||||
self::assertCount(2, $ticket->getPersons());
|
||||
}
|
||||
|
||||
public function testHandleExistingPersonIsNotRemovedNorCreatingDouble(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
$history = new PersonHistory($person, $ticket, new \DateTimeImmutable('1 month ago'));
|
||||
$command = new SetPersonsCommand([$person]);
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->persist(Argument::that(function ($arg) use ($person) {
|
||||
return $arg instanceof PersonHistory && $arg->getPerson() === $person;
|
||||
}))->shouldNotBeCalled();
|
||||
|
||||
$handler = $this->buildHandler($entityManager->reveal());
|
||||
|
||||
$handler->handle($ticket, $command);
|
||||
|
||||
self::assertNull($history->getEndDate());
|
||||
self::assertCount(1, $ticket->getPersons());
|
||||
}
|
||||
|
||||
public function testHandleRemoveExistingPerson(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
$person2 = new Person();
|
||||
$history = new PersonHistory($person, $ticket, new \DateTimeImmutable('1 month ago'));
|
||||
$command = new SetPersonsCommand([$person2]);
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->persist(Argument::that(function ($arg) use ($person2) {
|
||||
return $arg instanceof PersonHistory && $arg->getPerson() === $person2;
|
||||
}))->shouldBeCalled();
|
||||
|
||||
$handler = $this->buildHandler($entityManager->reveal());
|
||||
|
||||
$handler->handle($ticket, $command);
|
||||
|
||||
self::assertNotNull($history->getEndDate());
|
||||
self::assertContains($person2, $ticket->getPersons());
|
||||
}
|
||||
|
||||
public function testAddingDoublingPersonsDoesNotCreateDoubleHistories(): void
|
||||
{
|
||||
$ticket = new Ticket();
|
||||
$person = new Person();
|
||||
$command = new SetPersonsCommand([$person, $person]);
|
||||
|
||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||
$entityManager->persist(Argument::that(function ($arg) use ($person) {
|
||||
return $arg instanceof PersonHistory && $arg->getPerson() === $person;
|
||||
}))->shouldBeCalledOnce();
|
||||
|
||||
$handler = $this->buildHandler($entityManager->reveal());
|
||||
|
||||
$handler->handle($ticket, $command);
|
||||
|
||||
self::assertCount(1, $ticket->getPersons());
|
||||
}
|
||||
|
||||
private function buildHandler(EntityManagerInterface $entityManager): SetPersonsCommandHandler
|
||||
{
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->getUser()->willReturn(new User());
|
||||
|
||||
return new SetPersonsCommandHandler(new MockClock(), $entityManager, $security->reveal());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user