mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-07 12:48:24 +00:00
284 lines
11 KiB
PHP
284 lines
11 KiB
PHP
<?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\Serializer\Normalizer;
|
|
|
|
use Chill\MainBundle\Entity\User;
|
|
use Chill\MainBundle\Entity\UserGroup;
|
|
use Chill\PersonBundle\Entity\Person;
|
|
use Chill\TicketBundle\Entity\AddresseeHistory;
|
|
use Chill\TicketBundle\Entity\CallerHistory;
|
|
use Chill\TicketBundle\Entity\Comment;
|
|
use Chill\TicketBundle\Entity\EmergencyStatusHistory;
|
|
use Chill\TicketBundle\Entity\MotiveHistory;
|
|
use Chill\TicketBundle\Entity\PersonHistory;
|
|
use Chill\TicketBundle\Entity\StateHistory;
|
|
use Chill\TicketBundle\Entity\Ticket;
|
|
use Chill\TicketBundle\Security\Voter\CommentVoter;
|
|
use Symfony\Component\Security\Core\Security;
|
|
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
|
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
|
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
|
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
|
|
|
final class TicketNormalizer implements NormalizerInterface, NormalizerAwareInterface
|
|
{
|
|
use NormalizerAwareTrait;
|
|
|
|
public function __construct(private Security $security) {}
|
|
|
|
public function normalize($object, ?string $format = null, array $context = [])
|
|
{
|
|
if (!$object instanceof Ticket) {
|
|
throw new UnexpectedValueException();
|
|
}
|
|
|
|
$data = [
|
|
'type' => 'ticket_ticket',
|
|
'id' => $object->getId(),
|
|
'externalRef' => $object->getExternalRef(),
|
|
'createdAt' => $this->normalizer->normalize($object->getCreatedAt(), $format, $context),
|
|
'currentPersons' => $this->normalizer->normalize($object->getPersons(), $format, [
|
|
'groups' => 'read',
|
|
]),
|
|
'currentAddressees' => $this->normalizer->normalize($object->getCurrentAddressee(), $format, ['groups' => 'read']),
|
|
'currentInputs' => $this->normalizer->normalize($object->getCurrentInputs(), $format, ['groups' => 'read']),
|
|
'currentMotive' => $this->normalizer->normalize($object->getMotive(), $format, ['groups' => ['read', MotiveNormalizer::GROUP_CHILDREN_TO_PARENT]]),
|
|
'currentState' => $object->getState()?->value ?? 'open',
|
|
'emergency' => $object->getEmergencyStatus()?->value ?? 'no',
|
|
'caller' => $this->normalizer->normalize($object->getCaller(), $format, ['groups' => 'read']),
|
|
];
|
|
|
|
if ('read:simple' === $context['groups']) {
|
|
$data += ['type_extended' => 'ticket_ticket:simple'];
|
|
|
|
return $data;
|
|
}
|
|
|
|
$data += [
|
|
'type_extended' => 'ticket_ticket:extended',
|
|
'history' => array_values($this->serializeHistory($object, $format, ['groups' => 'read'])),
|
|
'updatedAt' => $this->normalizer->normalize($object->getUpdatedAt(), $format, $context),
|
|
'updatedBy' => $this->normalizer->normalize($object->getUpdatedBy(), $format, $context),
|
|
'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, $context),
|
|
];
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function supportsNormalization($data, ?string $format = null)
|
|
{
|
|
return 'json' === $format && $data instanceof Ticket;
|
|
}
|
|
|
|
private function serializeHistory(Ticket $ticket, string $format, array $context): array
|
|
{
|
|
$events = [
|
|
...array_map(
|
|
fn (StateHistory $stateHistory) => [
|
|
'event_type' => 'state_change',
|
|
'at' => $stateHistory->getStartDate(),
|
|
'by' => $stateHistory->getCreatedBy(),
|
|
'data' => [
|
|
'new_state' => $stateHistory->getState()->value,
|
|
],
|
|
],
|
|
$ticket->getStateHistories()->toArray(),
|
|
),
|
|
...array_map(
|
|
fn (MotiveHistory $motiveHistory) => [
|
|
'event_type' => 'set_motive',
|
|
'at' => $motiveHistory->getStartDate(),
|
|
'by' => $motiveHistory->getCreatedBy(),
|
|
'data' => $motiveHistory,
|
|
],
|
|
$ticket->getMotiveHistories()->toArray()
|
|
),
|
|
...array_map(
|
|
fn (PersonHistory $personHistory) => [
|
|
'event_type' => 'add_person',
|
|
'at' => $personHistory->getStartDate(),
|
|
'by' => $personHistory->getCreatedBy(),
|
|
'data' => $personHistory,
|
|
],
|
|
$ticket->getPersonHistories()->toArray(),
|
|
),
|
|
...array_map(
|
|
fn (Comment $comment) => [
|
|
'event_type' => 'add_comment',
|
|
'at' => $comment->getCreatedAt(),
|
|
'by' => $comment->getCreatedBy(),
|
|
'data' => $comment,
|
|
],
|
|
$ticket->getComments()->filter(fn (Comment $comment) => $this->security->isGranted(CommentVoter::READ, $comment))->toArray(),
|
|
),
|
|
...$this->addresseesStates($ticket),
|
|
...$this->personStates($ticket),
|
|
...array_map(
|
|
fn (EmergencyStatusHistory $stateHistory) => [
|
|
'event_type' => 'emergency_change',
|
|
'at' => $stateHistory->getStartDate(),
|
|
'by' => $stateHistory->getCreatedBy(),
|
|
'data' => [
|
|
'new_emergency' => $stateHistory->getEmergencyStatus()->value,
|
|
],
|
|
],
|
|
$ticket->getEmergencyStatusHistories()->toArray(),
|
|
),
|
|
...array_map(
|
|
fn (CallerHistory $stateHistory) => [
|
|
'event_type' => 'set_caller',
|
|
'at' => $stateHistory->getStartDate(),
|
|
'by' => $stateHistory->getCreatedBy(),
|
|
'data' => [
|
|
'new_caller' => $this->normalizer->normalize($ticket->getCaller(), $format, ['groups' => ['read']]),
|
|
],
|
|
],
|
|
$ticket->getCallerHistories()->toArray(),
|
|
),
|
|
];
|
|
|
|
if (null !== $ticket->getCreatedBy() && null !== $ticket->getCreatedAt()) {
|
|
$events[] =
|
|
[
|
|
'event_type' => 'create_ticket',
|
|
'at' => \DateTimeImmutable::createFromInterface($ticket->getCreatedAt())
|
|
->sub(new \DateInterval('PT1S')), // TODO hack to avoid collision with creation of the ticket event,
|
|
'by' => $ticket->getCreatedBy(),
|
|
'data' => [],
|
|
];
|
|
}
|
|
|
|
usort(
|
|
$events,
|
|
static fn (array $a, array $b): int => $a['at'] <=> $b['at']
|
|
);
|
|
|
|
return array_map(
|
|
fn ($data) => [
|
|
'event_type' => $data['event_type'],
|
|
'at' => $this->normalizer->normalize($data['at'], $format, $context),
|
|
'by' => $this->normalizer->normalize($data['by'], $format, $context),
|
|
'data' => $this->normalizer->normalize($data['data'], $format, $this->contextByEventType($data['event_type'], $context)),
|
|
],
|
|
$events
|
|
);
|
|
}
|
|
|
|
private function contextByEventType(string $eventType, array $context): array
|
|
{
|
|
return match($eventType) {
|
|
'set_motive' => array_merge($context, ['groups' => ['read', MotiveNormalizer::GROUP_CHILDREN_TO_PARENT]]),
|
|
default => $context,
|
|
};
|
|
}
|
|
|
|
private function addresseesStates(Ticket $ticket): array
|
|
{
|
|
/** @var array{string, array{added: list<AddresseeHistory>, removed: list<AddresseeHistory>}} $changes */
|
|
$changes = [];
|
|
$dateFormat = 'Y-m-d-m-Y-H-i-s';
|
|
|
|
foreach ($ticket->getAddresseeHistories() as $history) {
|
|
$changes[$history->getStartDate()->format($dateFormat)]['added'][] = $history;
|
|
if (null !== $history->getEndDate()) {
|
|
$changes[$history->getEndDate()->format($dateFormat)]['removed'][] = $history;
|
|
}
|
|
}
|
|
|
|
ksort($changes);
|
|
|
|
$currents = [];
|
|
$steps = [];
|
|
foreach ($changes as $change) {
|
|
$historiesAdded = $change['added'] ?? [];
|
|
$historiesRemoved = $change['removed'] ?? [];
|
|
|
|
if (0 < count($historiesAdded)) {
|
|
$at = $historiesAdded[0]->getStartDate();
|
|
$by = $historiesAdded[0]->getCreatedBy();
|
|
} elseif (0 < count($historiesRemoved)) {
|
|
$at = $historiesRemoved[0]->getEndDate();
|
|
$by = $historiesRemoved[0]->getRemovedBy();
|
|
} else {
|
|
throw new \LogicException('it should have at least one history');
|
|
}
|
|
|
|
$removed = array_map(fn (AddresseeHistory $history) => $history->getAddressee(), $historiesRemoved);
|
|
$currents = array_filter($currents, fn (User|UserGroup $a) => !in_array($a, $removed, true));
|
|
foreach ($historiesAdded as $history) {
|
|
$currents[] = $history->getAddressee();
|
|
}
|
|
|
|
$steps[] = [
|
|
'event_type' => 'addressees_state',
|
|
'at' => $at,
|
|
'by' => $by,
|
|
'data' => [
|
|
'addressees' => array_values($currents),
|
|
],
|
|
];
|
|
}
|
|
|
|
return $steps;
|
|
}
|
|
|
|
private function personStates(Ticket $ticket): array
|
|
{
|
|
/** @var array{string, array{added: list<PersonHistory>, removed: list<PersonHistory>}} $changes */
|
|
$changes = [];
|
|
$dateFormat = 'Y-m-d-m-Y-H-i-s';
|
|
|
|
foreach ($ticket->getPersonHistories() as $history) {
|
|
$changes[$history->getStartDate()->format($dateFormat)]['added'][] = $history;
|
|
if (null !== $history->getEndDate()) {
|
|
$changes[$history->getEndDate()->format($dateFormat)]['removed'][] = $history;
|
|
}
|
|
}
|
|
|
|
ksort($changes);
|
|
|
|
$currents = [];
|
|
$steps = [];
|
|
foreach ($changes as $change) {
|
|
$historiesAdded = $change['added'] ?? [];
|
|
$historiesRemoved = $change['removed'] ?? [];
|
|
|
|
if (0 < count($historiesAdded)) {
|
|
$at = $historiesAdded[0]->getStartDate();
|
|
$by = $historiesAdded[0]->getCreatedBy();
|
|
} elseif (0 < count($historiesRemoved)) {
|
|
$at = $historiesRemoved[0]->getEndDate();
|
|
$by = $historiesRemoved[0]->getRemovedBy();
|
|
} else {
|
|
throw new \LogicException('it should have at least one history');
|
|
}
|
|
|
|
$removed = array_map(fn (PersonHistory $history) => $history->getPerson(), $historiesRemoved);
|
|
$currents = array_filter($currents, fn (Person $a) => !in_array($a, $removed, true));
|
|
foreach ($historiesAdded as $history) {
|
|
$currents[] = $history->getPerson();
|
|
}
|
|
|
|
$steps[] = [
|
|
'event_type' => 'persons_state',
|
|
'at' => $at,
|
|
'by' => $by,
|
|
'data' => [
|
|
'persons' => array_values($currents),
|
|
],
|
|
];
|
|
}
|
|
|
|
return $steps;
|
|
}
|
|
}
|