mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-15 06:44:24 +00:00
Serialization of tickets with history
This commit is contained in:
parent
670b8eb82b
commit
467bea7cde
@ -49,7 +49,6 @@ final readonly class ReplaceMotiveCommandHandler
|
|||||||
|
|
||||||
if ($readyToAdd) {
|
if ($readyToAdd) {
|
||||||
$history = new MotiveHistory($command->motive, $ticket, $this->clock->now());
|
$history = new MotiveHistory($command->motive, $ticket, $this->clock->now());
|
||||||
$ticket->addMotiveHistory($history);
|
|
||||||
$this->entityManager->persist($history);
|
$this->entityManager->persist($history);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ use Twig\Environment;
|
|||||||
class EditTicketController
|
class EditTicketController
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private Environment $templating,
|
private Environment $templating
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
#[Route('/{_locale}/ticket/ticket/{id}/edit', name: 'chill_ticket_ticket_edit')]
|
#[Route('/{_locale}/ticket/ticket/{id}/edit', name: 'chill_ticket_ticket_edit')]
|
||||||
@ -29,6 +29,9 @@ class EditTicketController
|
|||||||
return new Response(
|
return new Response(
|
||||||
$this->templating->render(
|
$this->templating->render(
|
||||||
'@ChillTicket/Ticket/edit.html.twig',
|
'@ChillTicket/Ticket/edit.html.twig',
|
||||||
|
[
|
||||||
|
'ticket' => $ticket,
|
||||||
|
]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,11 @@ namespace Chill\TicketBundle\Entity;
|
|||||||
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
use Chill\MainBundle\Doctrine\Model\TrackCreationInterface;
|
||||||
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'motives_history', schema: 'chill_ticket')]
|
#[ORM\Table(name: 'motives_history', schema: 'chill_ticket')]
|
||||||
|
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_motive_history' => MotiveHistory::class])]
|
||||||
class MotiveHistory implements TrackCreationInterface
|
class MotiveHistory implements TrackCreationInterface
|
||||||
{
|
{
|
||||||
use TrackCreationTrait;
|
use TrackCreationTrait;
|
||||||
@ -24,21 +26,27 @@ class MotiveHistory implements TrackCreationInterface
|
|||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private ?\DateTimeImmutable $endDate = null;
|
private ?\DateTimeImmutable $endDate = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[ORM\ManyToOne(targetEntity: Motive::class)]
|
#[ORM\ManyToOne(targetEntity: Motive::class)]
|
||||||
#[ORM\JoinColumn(nullable: false)]
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private Motive $motive,
|
private Motive $motive,
|
||||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||||
#[ORM\JoinColumn(nullable: false)]
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
private Ticket $ticket,
|
private Ticket $ticket,
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private \DateTimeImmutable $startDate = new \DateTimeImmutable('now')
|
private \DateTimeImmutable $startDate = new \DateTimeImmutable('now')
|
||||||
) {}
|
) {
|
||||||
|
$ticket->addMotiveHistory($this);
|
||||||
|
}
|
||||||
|
|
||||||
public function getEndDate(): ?\DateTimeImmutable
|
public function getEndDate(): ?\DateTimeImmutable
|
||||||
{
|
{
|
||||||
|
@ -16,9 +16,11 @@ use Chill\MainBundle\Doctrine\Model\TrackCreationTrait;
|
|||||||
use Chill\MainBundle\Entity\User;
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Component\Serializer\Annotation as Serializer;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
#[ORM\Table(name: 'person_history', schema: 'chill_ticket')]
|
#[ORM\Table(name: 'person_history', schema: 'chill_ticket')]
|
||||||
|
#[Serializer\DiscriminatorMap(typeProperty: 'type', mapping: ['ticket_person_history' => PersonHistory::class])]
|
||||||
class PersonHistory implements TrackCreationInterface
|
class PersonHistory implements TrackCreationInterface
|
||||||
{
|
{
|
||||||
use TrackCreationTrait;
|
use TrackCreationTrait;
|
||||||
@ -26,22 +28,27 @@ class PersonHistory implements TrackCreationInterface
|
|||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER, nullable: false)]
|
||||||
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
#[ORM\GeneratedValue(strategy: 'AUTO')]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private ?int $id = null;
|
private ?int $id = null;
|
||||||
|
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private ?\DateTimeImmutable $endDate = null;
|
private ?\DateTimeImmutable $endDate = null;
|
||||||
|
|
||||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||||
#[ORM\JoinColumn(nullable: true)]
|
#[ORM\JoinColumn(nullable: true)]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private ?User $removedBy = null;
|
private ?User $removedBy = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
#[ORM\ManyToOne(targetEntity: Person::class, fetch: 'EAGER')]
|
#[ORM\ManyToOne(targetEntity: Person::class, fetch: 'EAGER')]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private Person $person,
|
private Person $person,
|
||||||
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
#[ORM\ManyToOne(targetEntity: Ticket::class)]
|
||||||
#[ORM\JoinColumn(nullable: false)]
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
private Ticket $ticket,
|
private Ticket $ticket,
|
||||||
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: false)]
|
||||||
|
#[Serializer\Groups(['read'])]
|
||||||
private \DateTimeImmutable $startDate,
|
private \DateTimeImmutable $startDate,
|
||||||
) {
|
) {
|
||||||
// keep ticket instance in sync with this
|
// keep ticket instance in sync with this
|
||||||
|
@ -176,4 +176,12 @@ class Ticket implements TrackCreationInterface, TrackUpdateInterface
|
|||||||
{
|
{
|
||||||
return $this->motiveHistories;
|
return $this->motiveHistories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ReadableCollection<int, PersonHistory>
|
||||||
|
*/
|
||||||
|
public function getPersonHistories(): ReadableCollection
|
||||||
|
{
|
||||||
|
return $this->personHistories;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,52 @@
|
|||||||
import {TranslatableString} from "../../../../ChillMainBundle/Resources/public/types";
|
import {DateTime, TranslatableString, User} from "../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
import {Person} from "../../../../ChillPersonBundle/Resources/public/types";
|
||||||
export interface Ticket {
|
|
||||||
id: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Motive {
|
export interface Motive {
|
||||||
|
type: "ticket_motive"
|
||||||
id: number,
|
id: number,
|
||||||
active: boolean,
|
active: boolean,
|
||||||
label: TranslatableString
|
label: TranslatableString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TicketHistory<T extends string, D extends object> {
|
||||||
|
event_type: T,
|
||||||
|
at: DateTime,
|
||||||
|
by: User,
|
||||||
|
data: D
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PersonHistory {
|
||||||
|
type: "ticket_person_history",
|
||||||
|
id: number,
|
||||||
|
startDate: DateTime,
|
||||||
|
endDate: null|DateTime,
|
||||||
|
person: Person,
|
||||||
|
removedBy: null,
|
||||||
|
createdBy: User|null,
|
||||||
|
createdAt: DateTime|null
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MotiveHistory {
|
||||||
|
type: "ticket_motive_history",
|
||||||
|
id: number,
|
||||||
|
startDate: null,
|
||||||
|
endDate: null|DateTime,
|
||||||
|
motive: Motive,
|
||||||
|
createdBy: User|null,
|
||||||
|
createdAt: DateTime|null,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddPersonEvent extends TicketHistory<"add_person", PersonHistory> {};
|
||||||
|
interface SetMotiveEvent extends TicketHistory<"set_motive", MotiveHistory> {};
|
||||||
|
|
||||||
|
type TicketHistoryLine = AddPersonEvent | SetMotiveEvent;
|
||||||
|
|
||||||
|
export interface Ticket {
|
||||||
|
type: "ticket_ticket"
|
||||||
|
id: number
|
||||||
|
externalRef: string
|
||||||
|
currentPersons: Person[]
|
||||||
|
currentMotive: null|Motive
|
||||||
|
history: TicketHistoryLine[],
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -4,9 +4,30 @@ import VueToast from 'vue-toast-notification';
|
|||||||
import 'vue-toast-notification/dist/theme-sugar.css';
|
import 'vue-toast-notification/dist/theme-sugar.css';
|
||||||
import {messages} from "./i18n/messages";
|
import {messages} from "./i18n/messages";
|
||||||
import App from './App.vue';
|
import App from './App.vue';
|
||||||
|
import {Ticket} from "../../types";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
initialTicket: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const i18n = _createI18n(messages)
|
const i18n = _createI18n(messages)
|
||||||
|
|
||||||
|
// the initial ticket is serialized there:
|
||||||
|
console.log(window.initialTicket);
|
||||||
|
// to have js object
|
||||||
|
const ticket = JSON.parse(window.initialTicket) as Ticket;
|
||||||
|
console.log("the ticket for this app (at page loading)", ticket);
|
||||||
|
|
||||||
|
for (const eh of ticket.history) {
|
||||||
|
if (eh.event_type === 'add_person') {
|
||||||
|
console.log("add_person", eh.data.person);
|
||||||
|
} else if (eh.event_type === 'set_motive') {
|
||||||
|
console.log("set_motive", eh.data.motive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const _app = createApp({
|
const _app = createApp({
|
||||||
template: '<app></app>',
|
template: '<app></app>',
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
{{ parent() }}
|
{{ parent() }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.initialTicket = "{{ ticket|serialize('json', {'groups': 'read'})|escape('js') }}";
|
||||||
|
</script>
|
||||||
{{ encore_entry_script_tags('vue_ticket_app') }}
|
{{ encore_entry_script_tags('vue_ticket_app') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -0,0 +1,91 @@
|
|||||||
|
<?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\TicketBundle\Entity\MotiveHistory;
|
||||||
|
use Chill\TicketBundle\Entity\PersonHistory;
|
||||||
|
use Chill\TicketBundle\Entity\Ticket;
|
||||||
|
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 normalize($object, ?string $format = null, array $context = [])
|
||||||
|
{
|
||||||
|
if (!$object instanceof Ticket) {
|
||||||
|
throw new UnexpectedValueException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'type' => 'ticket_ticket',
|
||||||
|
'id' => $object->getId(),
|
||||||
|
'externalRef' => $object->getExternalRef(),
|
||||||
|
'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']),
|
||||||
|
'history' => array_values($this->serializeHistory($object, $format, ['groups' => 'read'])),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (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(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
usort(
|
||||||
|
$events,
|
||||||
|
static function (array $a, array $b): int {
|
||||||
|
return $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, $context),
|
||||||
|
],
|
||||||
|
$events
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,8 @@ services:
|
|||||||
Chill\TicketBundle\Action\Ticket\Handler\:
|
Chill\TicketBundle\Action\Ticket\Handler\:
|
||||||
resource: '../Action/Ticket/Handler/'
|
resource: '../Action/Ticket/Handler/'
|
||||||
|
|
||||||
|
Chill\TicketBundle\Serializer\:
|
||||||
|
resource: '../Serializer/'
|
||||||
|
|
||||||
Chill\TicketBundle\DataFixtures\:
|
Chill\TicketBundle\DataFixtures\:
|
||||||
resource: '../DataFixtures/'
|
resource: '../DataFixtures/'
|
||||||
|
@ -64,7 +64,7 @@ final class ReplaceMotiveCommandHandlerTest extends KernelTestCase
|
|||||||
{
|
{
|
||||||
$motive = new Motive();
|
$motive = new Motive();
|
||||||
$ticket = new Ticket();
|
$ticket = new Ticket();
|
||||||
$ticket->addMotiveHistory(new MotiveHistory(new Motive(), $ticket));
|
$history = new MotiveHistory(new Motive(), $ticket);
|
||||||
|
|
||||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||||
$entityManager->persist(Argument::that(static function ($arg) use ($motive): bool {
|
$entityManager->persist(Argument::that(static function ($arg) use ($motive): bool {
|
||||||
@ -87,7 +87,7 @@ final class ReplaceMotiveCommandHandlerTest extends KernelTestCase
|
|||||||
{
|
{
|
||||||
$motive = new Motive();
|
$motive = new Motive();
|
||||||
$ticket = new Ticket();
|
$ticket = new Ticket();
|
||||||
$ticket->addMotiveHistory(new MotiveHistory($motive, $ticket));
|
$history = new MotiveHistory($motive, $ticket);
|
||||||
|
|
||||||
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
$entityManager = $this->prophesize(EntityManagerInterface::class);
|
||||||
$entityManager->persist(Argument::that(static function ($arg) use ($motive): bool {
|
$entityManager->persist(Argument::that(static function ($arg) use ($motive): bool {
|
||||||
|
@ -11,8 +11,10 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\TicketBundle\Tests\Entity;
|
namespace Chill\TicketBundle\Tests\Entity;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
use Chill\TicketBundle\Entity\Motive;
|
use Chill\TicketBundle\Entity\Motive;
|
||||||
use Chill\TicketBundle\Entity\MotiveHistory;
|
use Chill\TicketBundle\Entity\MotiveHistory;
|
||||||
|
use Chill\TicketBundle\Entity\PersonHistory;
|
||||||
use Chill\TicketBundle\Entity\Ticket;
|
use Chill\TicketBundle\Entity\Ticket;
|
||||||
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
@ -31,7 +33,6 @@ class TicketTest extends KernelTestCase
|
|||||||
self::assertNull($ticket->getMotive());
|
self::assertNull($ticket->getMotive());
|
||||||
|
|
||||||
$history = new MotiveHistory($motive, $ticket);
|
$history = new MotiveHistory($motive, $ticket);
|
||||||
$ticket->addMotiveHistory($history);
|
|
||||||
|
|
||||||
self::assertSame($motive, $ticket->getMotive());
|
self::assertSame($motive, $ticket->getMotive());
|
||||||
self::assertCount(1, $ticket->getMotiveHistories());
|
self::assertCount(1, $ticket->getMotiveHistories());
|
||||||
@ -39,9 +40,22 @@ class TicketTest extends KernelTestCase
|
|||||||
// replace motive
|
// replace motive
|
||||||
$motive2 = new Motive();
|
$motive2 = new Motive();
|
||||||
$history->setEndDate(new \DateTimeImmutable());
|
$history->setEndDate(new \DateTimeImmutable());
|
||||||
$ticket->addMotiveHistory(new MotiveHistory($motive2, $ticket));
|
$history2 = new MotiveHistory($motive2, $ticket);
|
||||||
|
|
||||||
self::assertCount(2, $ticket->getMotiveHistories());
|
self::assertCount(2, $ticket->getMotiveHistories());
|
||||||
self::assertSame($motive2, $ticket->getMotive());
|
self::assertSame($motive2, $ticket->getMotive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testGetPerson(): void
|
||||||
|
{
|
||||||
|
$ticket = new Ticket();
|
||||||
|
$person = new Person();
|
||||||
|
|
||||||
|
self::assertEquals([], $ticket->getPersons());
|
||||||
|
|
||||||
|
$history = new PersonHistory($person, $ticket, new \DateTimeImmutable('now'));
|
||||||
|
|
||||||
|
self::assertCount(1, $ticket->getPersons());
|
||||||
|
self::assertSame($person, $ticket->getPersons()[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
<?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\Serializer\Normalizer;
|
||||||
|
|
||||||
|
use Chill\PersonBundle\Entity\Person;
|
||||||
|
use Chill\TicketBundle\Entity\Motive;
|
||||||
|
use Chill\TicketBundle\Entity\MotiveHistory;
|
||||||
|
use Chill\TicketBundle\Entity\PersonHistory;
|
||||||
|
use Chill\TicketBundle\Entity\Ticket;
|
||||||
|
use Chill\TicketBundle\Serializer\Normalizer\TicketNormalizer;
|
||||||
|
use Prophecy\Argument;
|
||||||
|
use Prophecy\PhpUnit\ProphecyTrait;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class TicketNormalizerTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
use ProphecyTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideTickets
|
||||||
|
*/
|
||||||
|
public function testNormalize(Ticket $ticket, array $expected): void
|
||||||
|
{
|
||||||
|
$actual = $this->buildNormalizer()->normalize($ticket, 'json', ['groups' => 'read']);
|
||||||
|
|
||||||
|
self::assertEqualsCanonicalizing(array_keys($expected), array_keys($actual));
|
||||||
|
|
||||||
|
foreach (array_keys($expected) as $k) {
|
||||||
|
if ('history' === $k) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self::assertEqualsCanonicalizing($expected[$k], $actual[$k], sprintf("assert the content of the '%s' key", $k));
|
||||||
|
}
|
||||||
|
|
||||||
|
self::assertArrayHasKey('history', $actual);
|
||||||
|
self::assertIsArray($actual['history']);
|
||||||
|
|
||||||
|
foreach ($actual['history'] as $k => $eventType) {
|
||||||
|
self::assertEquals($expected['history'][$k]['event_type'], $eventType['event_type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildNormalizer(): TicketNormalizer
|
||||||
|
{
|
||||||
|
$normalizer = $this->prophesize(NormalizerInterface::class);
|
||||||
|
|
||||||
|
// empty array
|
||||||
|
$normalizer->normalize(
|
||||||
|
Argument::that(fn ($arg) => is_array($arg) && 0 === count($arg)),
|
||||||
|
'json',
|
||||||
|
Argument::type('array')
|
||||||
|
)->willReturn([]);
|
||||||
|
|
||||||
|
// array of mixed objects
|
||||||
|
$normalizer->normalize(
|
||||||
|
Argument::that(fn ($arg) => is_array($arg) && 0 < count($arg) && is_object($arg[0])),
|
||||||
|
'json',
|
||||||
|
Argument::type('array')
|
||||||
|
)->will(function ($args) {
|
||||||
|
return array_fill(0, count($args[0]), 'embedded');
|
||||||
|
});
|
||||||
|
|
||||||
|
// array of event type
|
||||||
|
$normalizer->normalize(
|
||||||
|
Argument::that(fn ($arg) => is_array($arg) && 0 < count($arg) && is_array($arg[0]) && array_key_exists('event_type', $arg[0])),
|
||||||
|
'json',
|
||||||
|
Argument::type('array')
|
||||||
|
)->will(function ($args): array {
|
||||||
|
$events = [];
|
||||||
|
|
||||||
|
foreach ($args[0] as $event) {
|
||||||
|
$events[] = $event['event_type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $events;
|
||||||
|
});
|
||||||
|
|
||||||
|
// datetime
|
||||||
|
$normalizer->normalize(Argument::type(\DateTimeImmutable::class), 'json', Argument::type('array'))
|
||||||
|
->will(function ($args) { return $args[0]->getTimestamp(); });
|
||||||
|
$normalizer->normalize(Argument::type(Motive::class), 'json', Argument::type('array'))->willReturn(['type' => 'motive', 'id' => 0]);
|
||||||
|
$normalizer->normalize(Argument::type(PersonHistory::class), 'json', Argument::type('array'))
|
||||||
|
->willReturn(['personHistory']);
|
||||||
|
$normalizer->normalize(Argument::type(MotiveHistory::class), 'json', Argument::type('array'))
|
||||||
|
->willReturn(['motiveHistory']);
|
||||||
|
$normalizer->normalize(null, 'json', Argument::type('array'))->willReturn(null);
|
||||||
|
|
||||||
|
$ticketNormalizer = new TicketNormalizer();
|
||||||
|
$ticketNormalizer->setNormalizer($normalizer->reveal());
|
||||||
|
|
||||||
|
return $ticketNormalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function provideTickets(): iterable
|
||||||
|
{
|
||||||
|
yield [
|
||||||
|
new Ticket(),
|
||||||
|
[
|
||||||
|
'type' => 'ticket_ticket',
|
||||||
|
'id' => null,
|
||||||
|
'externalRef' => '',
|
||||||
|
'currentPersons' => [],
|
||||||
|
'currentAddressees' => [],
|
||||||
|
'currentInputs' => [],
|
||||||
|
'currentMotive' => null,
|
||||||
|
'history' => [],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$ticket = new Ticket();
|
||||||
|
$ticket->setExternalRef('2134');
|
||||||
|
$personHistory = new PersonHistory(new Person(), $ticket, new \DateTimeImmutable('2024-04-01T12:00:00'));
|
||||||
|
$ticketHistory = new MotiveHistory(new Motive(), $ticket, new \DateTimeImmutable('2024-04-01T12:02:00'));
|
||||||
|
|
||||||
|
yield [
|
||||||
|
$ticket,
|
||||||
|
[
|
||||||
|
'type' => 'ticket_ticket',
|
||||||
|
'id' => null,
|
||||||
|
'externalRef' => '2134',
|
||||||
|
'currentPersons' => ['embedded'],
|
||||||
|
'currentAddressees' => [],
|
||||||
|
'currentInputs' => [],
|
||||||
|
'currentMotive' => ['type' => 'motive', 'id' => 0],
|
||||||
|
'history' => [['event_type' => 'add_person'], ['event_type' => 'set_motive']],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user