'ipartial', 'reference' => 'ipartial', 'typeComposant' => 'exact', 'typeComposant.name' => 'ipartial'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'createdAt'])] #[ApiResource( description: 'Composants du catalogue. Un composant représente un élément fonctionnel rattaché à une machine, avec un type, des fournisseurs et des documents.', operations: [ new Get(security: "is_granted('ROLE_VIEWER')"), new GetCollection(security: "is_granted('ROLE_VIEWER')"), new Post(security: "is_granted('ROLE_GESTIONNAIRE')"), new Put(security: "is_granted('ROLE_GESTIONNAIRE')"), new Patch(security: "is_granted('ROLE_GESTIONNAIRE')"), new Delete(security: "is_granted('ROLE_GESTIONNAIRE')"), ], normalizationContext: ['groups' => ['composant:read']], paginationClientItemsPerPage: true, paginationMaximumItemsPerPage: 200 )] class Composant { use CuidEntityTrait; #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] #[Groups(['composant:read', 'document:list'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, unique: true)] #[Groups(['composant:read', 'document:list'])] private string $name; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] #[Groups(['composant:read'])] private ?string $reference = null; #[ORM\Column(type: Types::TEXT, nullable: true)] #[Groups(['composant:read'])] private ?string $description = null; #[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 2, nullable: true)] #[Groups(['composant:read'])] private ?string $prix = null; #[ORM\ManyToOne(targetEntity: ModelType::class, inversedBy: 'composants')] #[ORM\JoinColumn(name: 'typeComposantId', referencedColumnName: 'id', nullable: true)] #[Groups(['composant:read'])] private ?ModelType $typeComposant = null; #[ORM\ManyToOne(targetEntity: Product::class, inversedBy: 'composants')] #[ORM\JoinColumn(name: 'productId', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')] #[Groups(['composant:read'])] private ?Product $product = null; /** * @var Collection */ #[ORM\ManyToMany(targetEntity: Constructeur::class, inversedBy: 'composants')] #[ORM\JoinTable( name: '_ComposantConstructeurs', joinColumns: [new ORM\JoinColumn(name: 'A', referencedColumnName: 'id', onDelete: 'CASCADE')], inverseJoinColumns: [new ORM\InverseJoinColumn(name: 'B', referencedColumnName: 'id', onDelete: 'CASCADE')] )] #[Groups(['composant:read'])] private Collection $constructeurs; /** * @var Collection */ #[ORM\OneToMany(mappedBy: 'composant', targetEntity: Document::class)] #[Groups(['composant:read'])] private Collection $documents; /** * @var Collection */ #[ORM\OneToMany(mappedBy: 'composant', targetEntity: CustomFieldValue::class)] #[Groups(['composant:read'])] private Collection $customFieldValues; /** * @var Collection */ #[ORM\OneToMany(mappedBy: 'composant', targetEntity: MachineComponentLink::class)] private Collection $machineLinks; /** * @var Collection */ #[ORM\OneToMany(targetEntity: ComposantPieceSlot::class, mappedBy: 'composant', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OrderBy(['position' => 'ASC'])] private Collection $pieceSlots; /** * @var Collection */ #[ORM\OneToMany(targetEntity: ComposantSubcomponentSlot::class, mappedBy: 'composant', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OrderBy(['position' => 'ASC'])] private Collection $subcomponentSlots; /** * @var Collection */ #[ORM\OneToMany(targetEntity: ComposantProductSlot::class, mappedBy: 'composant', cascade: ['persist', 'remove'], orphanRemoval: true)] #[ORM\OrderBy(['position' => 'ASC'])] private Collection $productSlots; #[ORM\Column(type: Types::INTEGER, options: ['default' => 1])] #[Groups(['composant:read'])] private int $version = 1; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'createdAt')] #[Groups(['composant:read'])] private DateTimeImmutable $createdAt; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, name: 'updatedAt')] #[Groups(['composant:read'])] private DateTimeImmutable $updatedAt; public function __construct() { $this->createdAt = new DateTimeImmutable(); $this->updatedAt = new DateTimeImmutable(); $this->constructeurs = new ArrayCollection(); $this->documents = new ArrayCollection(); $this->customFieldValues = new ArrayCollection(); $this->machineLinks = new ArrayCollection(); $this->pieceSlots = new ArrayCollection(); $this->subcomponentSlots = new ArrayCollection(); $this->productSlots = new ArrayCollection(); } public function getName(): string { return $this->name; } public function setName(string $name): static { $this->name = mb_strtoupper(mb_substr($name, 0, 1)).mb_substr($name, 1); return $this; } public function getReference(): ?string { return $this->reference; } public function setReference(?string $reference): static { $this->reference = $reference; return $this; } public function getDescription(): ?string { return $this->description; } public function setDescription(?string $description): static { $this->description = $description; return $this; } public function getPrix(): ?string { return $this->prix; } public function setPrix(?string $prix): static { $this->prix = $prix; return $this; } public function getTypeComposant(): ?ModelType { return $this->typeComposant; } public function setTypeComposant(?ModelType $typeComposant): static { $this->typeComposant = $typeComposant; return $this; } public function getProduct(): ?Product { return $this->product; } public function setProduct(?Product $product): static { $this->product = $product; return $this; } /** * @return Collection */ public function getConstructeurs(): Collection { return $this->constructeurs; } /** * @param iterable $constructeurs */ public function setConstructeurs(iterable $constructeurs): static { $this->constructeurs = new ArrayCollection(); foreach ($constructeurs as $constructeur) { if ($constructeur instanceof Constructeur && !$this->constructeurs->contains($constructeur)) { $this->constructeurs->add($constructeur); } } return $this; } public function addConstructeur(Constructeur $constructeur): static { if (!$this->constructeurs->contains($constructeur)) { $this->constructeurs->add($constructeur); } return $this; } public function removeConstructeur(Constructeur $constructeur): static { $this->constructeurs->removeElement($constructeur); return $this; } /** * @return Collection */ public function getDocuments(): Collection { return $this->documents; } /** * @return Collection */ public function getCustomFieldValues(): Collection { return $this->customFieldValues; } /** * @return Collection */ public function getPieceSlots(): Collection { return $this->pieceSlots; } public function addPieceSlot(ComposantPieceSlot $pieceSlot): static { if (!$this->pieceSlots->contains($pieceSlot)) { $this->pieceSlots->add($pieceSlot); $pieceSlot->setComposant($this); } return $this; } public function removePieceSlot(ComposantPieceSlot $pieceSlot): static { $this->pieceSlots->removeElement($pieceSlot); return $this; } /** * @return Collection */ public function getSubcomponentSlots(): Collection { return $this->subcomponentSlots; } public function addSubcomponentSlot(ComposantSubcomponentSlot $subcomponentSlot): static { if (!$this->subcomponentSlots->contains($subcomponentSlot)) { $this->subcomponentSlots->add($subcomponentSlot); $subcomponentSlot->setComposant($this); } return $this; } public function removeSubcomponentSlot(ComposantSubcomponentSlot $subcomponentSlot): static { $this->subcomponentSlots->removeElement($subcomponentSlot); return $this; } /** * @return Collection */ public function getProductSlots(): Collection { return $this->productSlots; } /** * Virtual property — rebuilds the legacy structure JSON from slot tables. * * @return null|array{pieces: list>, products: list>, subcomponents: list>} */ #[Groups(['composant:read'])] public function getStructure(): ?array { $pieces = []; foreach ($this->pieceSlots as $slot) { $pieces[] = [ 'slotId' => $slot->getId(), 'typePieceId' => $slot->getTypePiece()?->getId(), 'selectedPieceId' => $slot->getSelectedPiece()?->getId(), 'quantity' => $slot->getQuantity(), 'position' => $slot->getPosition(), ]; } $products = []; foreach ($this->productSlots as $slot) { $products[] = [ 'slotId' => $slot->getId(), 'typeProductId' => $slot->getTypeProduct()?->getId(), 'selectedProductId' => $slot->getSelectedProduct()?->getId(), 'familyCode' => $slot->getFamilyCode(), 'position' => $slot->getPosition(), ]; } $subcomponents = []; foreach ($this->subcomponentSlots as $slot) { $subcomponents[] = [ 'slotId' => $slot->getId(), 'alias' => $slot->getAlias(), 'familyCode' => $slot->getFamilyCode(), 'typeComposantId' => $slot->getTypeComposant()?->getId(), 'selectedComponentId' => $slot->getSelectedComposant()?->getId(), 'position' => $slot->getPosition(), ]; } if (empty($pieces) && empty($products) && empty($subcomponents)) { return null; } return [ 'pieces' => $pieces, 'products' => $products, 'subcomponents' => $subcomponents, ]; } public function addProductSlot(ComposantProductSlot $productSlot): static { if (!$this->productSlots->contains($productSlot)) { $this->productSlots->add($productSlot); $productSlot->setComposant($this); } return $this; } public function removeProductSlot(ComposantProductSlot $productSlot): static { $this->productSlots->removeElement($productSlot); return $this; } public function getVersion(): int { return $this->version; } public function incrementVersion(): static { ++$this->version; return $this; } }