From 91b8b424d6dfecea8455951373d5d1c83c3cffa1 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 11 Feb 2026 17:16:27 +0100 Subject: [PATCH] fix(documents) : add serialization groups to prevent OOM on collection endpoint The path field (base64 data URIs) is now excluded from GetCollection via document:list group. Individual GET returns path via document:detail group. Related entities expose id+name in document:list for attachment display. Frontend lazy-loads path on download/preview click. Co-Authored-By: Claude Opus 4.6 --- Inventory_frontend | 2 +- src/Entity/Composant.php | 4 ++-- src/Entity/Document.php | 30 ++++++++++++++++++++++++------ src/Entity/Machine.php | 38 +++++++++++++++++++++----------------- src/Entity/Piece.php | 4 ++-- src/Entity/Product.php | 4 ++-- src/Entity/Site.php | 3 +++ 7 files changed, 55 insertions(+), 30 deletions(-) diff --git a/Inventory_frontend b/Inventory_frontend index 439db81..5cab154 160000 --- a/Inventory_frontend +++ b/Inventory_frontend @@ -1 +1 @@ -Subproject commit 439db8117ab3f9dd932de92e1d9932889daab14a +Subproject commit 5cab15422dd7b873801eb8b05f82c94a5d213787 diff --git a/src/Entity/Composant.php b/src/Entity/Composant.php index 2573e15..7552ca2 100644 --- a/src/Entity/Composant.php +++ b/src/Entity/Composant.php @@ -30,11 +30,11 @@ class Composant { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] - #[Groups(['composant:read'])] + #[Groups(['composant:read', 'document:list'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, unique: true)] - #[Groups(['composant:read'])] + #[Groups(['composant:read', 'document:list'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] diff --git a/src/Entity/Document.php b/src/Entity/Document.php index fb0ad3a..be7728b 100644 --- a/src/Entity/Document.php +++ b/src/Entity/Document.php @@ -5,6 +5,11 @@ declare(strict_types=1); namespace App\Entity; use ApiPlatform\Metadata\ApiResource; +use ApiPlatform\Metadata\Delete; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Post; +use ApiPlatform\Metadata\Put; use App\Repository\DocumentRepository; use DateTimeImmutable; use Doctrine\DBAL\Types\Types; @@ -15,6 +20,13 @@ use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Table(name: 'documents')] #[ORM\HasLifecycleCallbacks] #[ApiResource( + operations: [ + new GetCollection(normalizationContext: ['groups' => ['document:list']]), + new Get(normalizationContext: ['groups' => ['document:list', 'document:detail']]), + new Post(), + new Put(), + new Delete(), + ], paginationClientItemsPerPage: true, paginationMaximumItemsPerPage: 200 )] @@ -22,50 +34,56 @@ class Document { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] - #[Groups(['document:read', 'composant:read', 'piece:read', 'product:read'])] + #[Groups(['document:list', 'document:read', 'composant:read', 'piece:read', 'product:read'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255)] - #[Groups(['document:read', 'composant:read', 'piece:read', 'product:read'])] + #[Groups(['document:list', 'document:read', 'composant:read', 'piece:read', 'product:read'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255)] - #[Groups(['document:read', 'composant:read', 'piece:read', 'product:read'])] + #[Groups(['document:list', 'document:read', 'composant:read', 'piece:read', 'product:read'])] private string $filename; #[ORM\Column(type: Types::TEXT)] - #[Groups(['document:read', 'composant:read', 'piece:read', 'product:read'])] + #[Groups(['document:detail', 'document:read', 'composant:read', 'piece:read', 'product:read'])] private string $path; #[ORM\Column(type: Types::STRING, length: 100, name: 'mimeType')] - #[Groups(['document:read', 'composant:read', 'piece:read', 'product:read'])] + #[Groups(['document:list', 'document:read', 'composant:read', 'piece:read', 'product:read'])] private string $mimeType; #[ORM\Column(type: Types::INTEGER)] - #[Groups(['document:read', 'composant:read', 'piece:read', 'product:read'])] + #[Groups(['document:list', 'document:read', 'composant:read', 'piece:read', 'product:read'])] private int $size; #[ORM\ManyToOne(targetEntity: Machine::class, inversedBy: 'documents')] #[ORM\JoinColumn(name: 'machineId', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] + #[Groups(['document:list'])] private ?Machine $machine = null; #[ORM\ManyToOne(targetEntity: Composant::class, inversedBy: 'documents')] #[ORM\JoinColumn(name: 'composantId', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] + #[Groups(['document:list'])] private ?Composant $composant = null; #[ORM\ManyToOne(targetEntity: Piece::class, inversedBy: 'documents')] #[ORM\JoinColumn(name: 'pieceId', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] + #[Groups(['document:list'])] private ?Piece $piece = null; #[ORM\ManyToOne(targetEntity: Product::class, inversedBy: 'documents')] #[ORM\JoinColumn(name: 'productId', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] + #[Groups(['document:list'])] private ?Product $product = null; #[ORM\ManyToOne(targetEntity: Site::class, inversedBy: 'documents')] #[ORM\JoinColumn(name: 'siteId', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')] + #[Groups(['document:list'])] private ?Site $site = null; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] + #[Groups(['document:list'])] private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] diff --git a/src/Entity/Machine.php b/src/Entity/Machine.php index ea280c1..72ad4f6 100644 --- a/src/Entity/Machine.php +++ b/src/Entity/Machine.php @@ -6,10 +6,12 @@ namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use App\Repository\MachineRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity(repositoryClass: MachineRepository::class)] #[ORM\Table(name: 'machines')] @@ -19,9 +21,11 @@ class Machine { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] + #[Groups(['document:list'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, unique: true)] + #[Groups(['document:list'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] @@ -80,29 +84,29 @@ class Machine private Collection $customFieldValues; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] - private \DateTimeImmutable $createdAt; + private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] - private \DateTimeImmutable $updatedAt; + private DateTimeImmutable $updatedAt; public function __construct() { - $this->constructeurs = new ArrayCollection(); - $this->componentLinks = new ArrayCollection(); - $this->pieceLinks = new ArrayCollection(); - $this->productLinks = new ArrayCollection(); - $this->documents = new ArrayCollection(); + $this->constructeurs = new ArrayCollection(); + $this->componentLinks = new ArrayCollection(); + $this->pieceLinks = new ArrayCollection(); + $this->productLinks = new ArrayCollection(); + $this->documents = new ArrayCollection(); $this->customFieldValues = new ArrayCollection(); } #[ORM\PrePersist] public function setCreatedAtValue(): void { - $now = new \DateTimeImmutable(); + $now = new DateTimeImmutable(); $this->createdAt = $now; $this->updatedAt = $now; - if ($this->id === null) { + if (null === $this->id) { $this->id = $this->generateCuid(); } } @@ -110,12 +114,7 @@ class Machine #[ORM\PreUpdate] public function setUpdatedAtValue(): void { - $this->updatedAt = new \DateTimeImmutable(); - } - - private function generateCuid(): string - { - return 'cl' . bin2hex(random_bytes(12)); + $this->updatedAt = new DateTimeImmutable(); } public function getId(): ?string @@ -238,13 +237,18 @@ class Machine return $this->customFieldValues; } - public function getCreatedAt(): \DateTimeImmutable + public function getCreatedAt(): DateTimeImmutable { return $this->createdAt; } - public function getUpdatedAt(): \DateTimeImmutable + public function getUpdatedAt(): DateTimeImmutable { return $this->updatedAt; } + + private function generateCuid(): string + { + return 'cl'.bin2hex(random_bytes(12)); + } } diff --git a/src/Entity/Piece.php b/src/Entity/Piece.php index dc2a29a..f8b4073 100644 --- a/src/Entity/Piece.php +++ b/src/Entity/Piece.php @@ -30,11 +30,11 @@ class Piece { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] - #[Groups(['piece:read'])] + #[Groups(['piece:read', 'document:list'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, unique: true)] - #[Groups(['piece:read'])] + #[Groups(['piece:read', 'document:list'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] diff --git a/src/Entity/Product.php b/src/Entity/Product.php index abb9100..fa45ecf 100644 --- a/src/Entity/Product.php +++ b/src/Entity/Product.php @@ -30,11 +30,11 @@ class Product { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] - #[Groups(['product:read'])] + #[Groups(['product:read', 'document:list'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, unique: true)] - #[Groups(['product:read'])] + #[Groups(['product:read', 'document:list'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] diff --git a/src/Entity/Site.php b/src/Entity/Site.php index c01af47..e1e8879 100644 --- a/src/Entity/Site.php +++ b/src/Entity/Site.php @@ -16,6 +16,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Attribute\Groups; use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: SiteRepository::class)] @@ -36,10 +37,12 @@ class Site { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] + #[Groups(['document:list'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255)] #[Assert\NotBlank] + #[Groups(['document:list'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255, options: ['default' => ''], name: 'contactName')]