From 86447000b1e170fb7832a3ec809949830e59b89f Mon Sep 17 00:00:00 2001 From: r-dev Date: Fri, 23 Jan 2026 19:35:26 +0100 Subject: [PATCH] feat(api) : ajoute pagination et filtres pour les catalogues --- makefile | 13 +++-- src/Entity/Composant.php | 38 +++++++++------ src/Entity/Constructeur.php | 36 ++++++++------ src/Entity/ModelType.php | 97 ++++++++++++++++++++----------------- src/Entity/Piece.php | 38 +++++++++------ src/Entity/Product.php | 42 +++++++++------- src/Entity/Site.php | 35 +++++++------ src/Entity/TypeMachine.php | 18 ++++--- 8 files changed, 180 insertions(+), 137 deletions(-) diff --git a/makefile b/makefile index 1fdb6e2..f477b03 100644 --- a/makefile +++ b/makefile @@ -130,15 +130,18 @@ import-data: # Fixtures management fixtures-dump: @echo "Dumping current database to fixtures/data.sql..." - $(DOCKER_COMPOSE) exec -T db pg_dump -U $(POSTGRES_USER) -d $(POSTGRES_DB) --data-only --inserts --no-owner --no-privileges | grep -v "^pg_dump:" | grep -v "^\\\\restrict" > fixtures/data.sql - @echo "Fixtures saved to fixtures/data.sql ($(shell wc -l < fixtures/data.sql) lines)" + $(DOCKER_COMPOSE) exec -T db pg_dump -U $(POSTGRES_USER) -d $(POSTGRES_DB) \ + --data-only --inserts --no-owner --no-privileges \ + --exclude-table=doctrine_migration_versions \ + | grep -v "^pg_dump:" | grep -v "^\\\\restrict" > fixtures/data.sql + @echo "Fixtures saved to fixtures/data.sql" fixtures-load: - @echo "Loading fixtures from fixtures/data.sql..." + @echo "Loading fixtures from fixtures/data.sql (FK checks disabled)..." $(DOCKER_COMPOSE) exec -T db psql -U $(POSTGRES_USER) -d $(POSTGRES_DB) -c "SET session_replication_role = replica;" - $(DOCKER_COMPOSE) exec -T db psql -U $(POSTGRES_USER) -d $(POSTGRES_DB) < fixtures/data.sql + -$(DOCKER_COMPOSE) exec -T db psql -U $(POSTGRES_USER) -d $(POSTGRES_DB) < fixtures/data.sql $(DOCKER_COMPOSE) exec -T db psql -U $(POSTGRES_USER) -d $(POSTGRES_DB) -c "SET session_replication_role = DEFAULT;" - @echo "Fixtures loaded successfully!" + @echo "Fixtures loaded!" fixtures-reset: @echo "Resetting database and loading fixtures..." diff --git a/src/Entity/Composant.php b/src/Entity/Composant.php index a13d2d1..2eb4267 100644 --- a/src/Entity/Composant.php +++ b/src/Entity/Composant.php @@ -4,8 +4,12 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; +use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use App\Repository\ComposantRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; @@ -15,8 +19,12 @@ use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity(repositoryClass: ComposantRepository::class)] #[ORM\Table(name: 'composants')] #[ORM\HasLifecycleCallbacks] +#[ApiFilter(SearchFilter::class, properties: ['name' => 'partial', 'reference' => 'partial'])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'createdAt'])] #[ApiResource( normalizationContext: ['groups' => ['composant:read']], + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 )] class Composant { @@ -85,28 +93,28 @@ class Composant #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] #[Groups(['composant:read'])] - private \DateTimeImmutable $createdAt; + private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] #[Groups(['composant:read'])] - private \DateTimeImmutable $updatedAt; + private DateTimeImmutable $updatedAt; public function __construct() { - $this->constructeurs = new ArrayCollection(); - $this->documents = new ArrayCollection(); + $this->constructeurs = new ArrayCollection(); + $this->documents = new ArrayCollection(); $this->customFieldValues = new ArrayCollection(); - $this->machineLinks = new ArrayCollection(); + $this->machineLinks = 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(); } } @@ -114,12 +122,7 @@ class Composant #[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 @@ -262,13 +265,18 @@ class Composant 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/Constructeur.php b/src/Entity/Constructeur.php index ef9ee93..84beceb 100644 --- a/src/Entity/Constructeur.php +++ b/src/Entity/Constructeur.php @@ -6,6 +6,7 @@ namespace App\Entity; use ApiPlatform\Metadata\ApiResource; use App\Repository\ConstructeurRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; @@ -14,7 +15,10 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity(repositoryClass: ConstructeurRepository::class)] #[ORM\Table(name: 'constructeurs')] #[ORM\HasLifecycleCallbacks] -#[ApiResource] +#[ApiResource( + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 +)] class Constructeur { #[ORM\Id] @@ -31,10 +35,10 @@ class Constructeur private ?string $phone = null; #[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; /** * @var Collection @@ -62,20 +66,20 @@ class Constructeur public function __construct() { - $this->machines = new ArrayCollection(); + $this->machines = new ArrayCollection(); $this->composants = new ArrayCollection(); - $this->pieces = new ArrayCollection(); - $this->products = new ArrayCollection(); + $this->pieces = new ArrayCollection(); + $this->products = 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(); } } @@ -83,12 +87,7 @@ class Constructeur #[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 @@ -139,13 +138,18 @@ class Constructeur return $this; } - 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/ModelType.php b/src/Entity/ModelType.php index 6638a15..5eb4ca4 100644 --- a/src/Entity/ModelType.php +++ b/src/Entity/ModelType.php @@ -9,6 +9,7 @@ use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use App\Enum\ModelCategory; use App\Repository\ModelTypeRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; @@ -20,7 +21,10 @@ use Symfony\Component\Serializer\Annotation\Groups; #[ORM\UniqueConstraint(name: 'unique_category_name', columns: ['category', 'name'])] #[ORM\HasLifecycleCallbacks] #[ApiFilter(SearchFilter::class, properties: ['category' => 'exact'])] -#[ApiResource] +#[ApiResource( + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 +)] class ModelType { #[ORM\Id] @@ -62,11 +66,11 @@ class ModelType #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] #[Groups(['model_type:read'])] - private \DateTimeImmutable $createdAt; + private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] #[Groups(['model_type:read'])] - private \DateTimeImmutable $updatedAt; + private DateTimeImmutable $updatedAt; private ?array $pendingStructure = null; @@ -126,25 +130,25 @@ class ModelType public function __construct() { - $this->composants = new ArrayCollection(); - $this->pieces = new ArrayCollection(); - $this->products = new ArrayCollection(); + $this->composants = new ArrayCollection(); + $this->pieces = new ArrayCollection(); + $this->products = new ArrayCollection(); $this->componentRequirements = new ArrayCollection(); - $this->pieceRequirements = new ArrayCollection(); - $this->productRequirements = new ArrayCollection(); - $this->customFields = new ArrayCollection(); - $this->pieceCustomFields = new ArrayCollection(); - $this->productCustomFields = new ArrayCollection(); + $this->pieceRequirements = new ArrayCollection(); + $this->productRequirements = new ArrayCollection(); + $this->customFields = new ArrayCollection(); + $this->pieceCustomFields = new ArrayCollection(); + $this->productCustomFields = 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(); } } @@ -152,12 +156,7 @@ class ModelType #[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 @@ -205,7 +204,7 @@ class ModelType { $this->category = $category; - if ($this->pendingStructure !== null) { + if (null !== $this->pendingStructure) { $this->applyStructureForCategory($this->pendingStructure, $category); $this->pendingStructure = null; } @@ -278,8 +277,8 @@ class ModelType { return match ($this->category) { ModelCategory::COMPONENT => $this->componentSkeleton, - ModelCategory::PIECE => $this->pieceSkeleton, - ModelCategory::PRODUCT => $this->productSkeleton, + ModelCategory::PIECE => $this->pieceSkeleton, + ModelCategory::PRODUCT => $this->productSkeleton, }; } @@ -288,6 +287,7 @@ class ModelType { if (!isset($this->category)) { $this->pendingStructure = $structure; + return $this; } @@ -296,34 +296,41 @@ class ModelType return $this; } - private function applyStructureForCategory(?array $structure, ModelCategory $category): void - { - if ($category === ModelCategory::COMPONENT) { - $this->componentSkeleton = $structure; - $this->pieceSkeleton = null; - $this->productSkeleton = null; - return; - } - - if ($category === ModelCategory::PIECE) { - $this->pieceSkeleton = $structure; - $this->componentSkeleton = null; - $this->productSkeleton = null; - return; - } - - $this->productSkeleton = $structure; - $this->componentSkeleton = null; - $this->pieceSkeleton = null; - } - - 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)); + } + + private function applyStructureForCategory(?array $structure, ModelCategory $category): void + { + if (ModelCategory::COMPONENT === $category) { + $this->componentSkeleton = $structure; + $this->pieceSkeleton = null; + $this->productSkeleton = null; + + return; + } + + if (ModelCategory::PIECE === $category) { + $this->pieceSkeleton = $structure; + $this->componentSkeleton = null; + $this->productSkeleton = null; + + return; + } + + $this->productSkeleton = $structure; + $this->componentSkeleton = null; + $this->pieceSkeleton = null; + } } diff --git a/src/Entity/Piece.php b/src/Entity/Piece.php index 51ec6e1..629b293 100644 --- a/src/Entity/Piece.php +++ b/src/Entity/Piece.php @@ -4,8 +4,12 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; +use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use App\Repository\PieceRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; @@ -15,8 +19,12 @@ use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity(repositoryClass: PieceRepository::class)] #[ORM\Table(name: 'pieces')] #[ORM\HasLifecycleCallbacks] +#[ApiFilter(SearchFilter::class, properties: ['name' => 'partial', 'reference' => 'partial'])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'createdAt'])] #[ApiResource( normalizationContext: ['groups' => ['piece:read']], + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 )] class Piece { @@ -81,28 +89,28 @@ class Piece #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] #[Groups(['piece:read'])] - private \DateTimeImmutable $createdAt; + private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] #[Groups(['piece:read'])] - private \DateTimeImmutable $updatedAt; + private DateTimeImmutable $updatedAt; public function __construct() { - $this->constructeurs = new ArrayCollection(); - $this->documents = new ArrayCollection(); + $this->constructeurs = new ArrayCollection(); + $this->documents = new ArrayCollection(); $this->customFieldValues = new ArrayCollection(); - $this->machineLinks = new ArrayCollection(); + $this->machineLinks = 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 +118,7 @@ class Piece #[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 @@ -230,13 +233,18 @@ class Piece 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/Product.php b/src/Entity/Product.php index 9f77066..419da86 100644 --- a/src/Entity/Product.php +++ b/src/Entity/Product.php @@ -4,8 +4,12 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; +use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; +use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use App\Repository\ProductRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; @@ -15,8 +19,12 @@ use Symfony\Component\Serializer\Attribute\Groups; #[ORM\Entity(repositoryClass: ProductRepository::class)] #[ORM\Table(name: 'products')] #[ORM\HasLifecycleCallbacks] +#[ApiFilter(SearchFilter::class, properties: ['name' => 'partial', 'reference' => 'partial'])] +#[ApiFilter(OrderFilter::class, properties: ['name', 'createdAt'])] #[ApiResource( normalizationContext: ['groups' => ['product:read']], + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 )] class Product { @@ -88,30 +96,30 @@ class Product #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] #[Groups(['product:read'])] - private \DateTimeImmutable $createdAt; + private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] #[Groups(['product:read'])] - private \DateTimeImmutable $updatedAt; + private DateTimeImmutable $updatedAt; public function __construct() { - $this->constructeurs = new ArrayCollection(); - $this->documents = new ArrayCollection(); + $this->constructeurs = new ArrayCollection(); + $this->documents = new ArrayCollection(); $this->customFieldValues = new ArrayCollection(); - $this->pieces = new ArrayCollection(); - $this->composants = new ArrayCollection(); - $this->machineLinks = new ArrayCollection(); + $this->pieces = new ArrayCollection(); + $this->composants = new ArrayCollection(); + $this->machineLinks = 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(); } } @@ -119,12 +127,7 @@ class Product #[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 @@ -227,13 +230,18 @@ class Product 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/Site.php b/src/Entity/Site.php index 6eb0990..bfc0b6c 100644 --- a/src/Entity/Site.php +++ b/src/Entity/Site.php @@ -11,6 +11,7 @@ use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Put; use App\Repository\SiteRepository; +use DateTimeImmutable; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\DBAL\Types\Types; @@ -27,7 +28,9 @@ use Symfony\Component\Validator\Constraints as Assert; new Post(), new Put(), new Delete(), - ] + ], + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 )] class Site { @@ -55,10 +58,10 @@ class Site private string $contactCity = ''; #[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; /** * @var Collection @@ -74,18 +77,18 @@ class Site public function __construct() { - $this->machines = new ArrayCollection(); + $this->machines = new ArrayCollection(); $this->documents = new ArrayCollection(); } #[ORM\PrePersist] public function setCreatedAtValue(): void { - $this->createdAt = new \DateTimeImmutable(); - $this->updatedAt = new \DateTimeImmutable(); + $this->createdAt = new DateTimeImmutable(); + $this->updatedAt = new DateTimeImmutable(); // Générer un ID CUID-compatible si nécessaire - if ($this->id === null) { + if (null === $this->id) { $this->id = $this->generateCuid(); } } @@ -93,13 +96,7 @@ class Site #[ORM\PreUpdate] public function setUpdatedAtValue(): void { - $this->updatedAt = new \DateTimeImmutable(); - } - - private function generateCuid(): string - { - // Génération d'un ID compatible CUID (format: cl + 24 caractères) - return 'cl' . bin2hex(random_bytes(12)); + $this->updatedAt = new DateTimeImmutable(); } // Getters et Setters @@ -188,12 +185,12 @@ class Site return $this; } - public function getCreatedAt(): \DateTimeImmutable + public function getCreatedAt(): DateTimeImmutable { return $this->createdAt; } - public function getUpdatedAt(): \DateTimeImmutable + public function getUpdatedAt(): DateTimeImmutable { return $this->updatedAt; } @@ -257,4 +254,10 @@ class Site return $this; } + + private function generateCuid(): string + { + // Génération d'un ID compatible CUID (format: cl + 24 caractères) + return 'cl'.bin2hex(random_bytes(12)); + } } diff --git a/src/Entity/TypeMachine.php b/src/Entity/TypeMachine.php index 69cdbdb..e6ee521 100644 --- a/src/Entity/TypeMachine.php +++ b/src/Entity/TypeMachine.php @@ -32,7 +32,9 @@ use Symfony\Component\Validator\Constraints as Assert; new Post(), new Put(), new Delete(), - ] + ], + paginationClientItemsPerPage: true, + paginationMaximumItemsPerPage: 500 )] class TypeMachine { @@ -118,14 +120,14 @@ class TypeMachine public function __construct() { - $this->id = 'cl' . bin2hex(random_bytes(12)); - $this->createdAt = new DateTimeImmutable(); - $this->updatedAt = new DateTimeImmutable(); - $this->machines = new ArrayCollection(); - $this->customFields = new ArrayCollection(); + $this->id = 'cl'.bin2hex(random_bytes(12)); + $this->createdAt = new DateTimeImmutable(); + $this->updatedAt = new DateTimeImmutable(); + $this->machines = new ArrayCollection(); + $this->customFields = new ArrayCollection(); $this->componentRequirements = new ArrayCollection(); - $this->pieceRequirements = new ArrayCollection(); - $this->productRequirements = new ArrayCollection(); + $this->pieceRequirements = new ArrayCollection(); + $this->productRequirements = new ArrayCollection(); } public function getId(): ?string