requireRole($this->security, 'ROLE_GESTIONNAIRE'); $source = $this->machineRepository->find($machineId); if (!$source instanceof Machine) { $this->mcpError('not_found', "Machine not found: {$machineId}"); } $site = $this->entityManager->getRepository(Site::class)->find($siteId); if (!$site) { $this->mcpError('not_found', "Site not found: {$siteId}"); } // Create new machine $newMachine = new Machine(); $newMachine->setName($name); $newMachine->setSite($site); if ('' !== $reference) { $newMachine->setReference($reference); } $newMachine->setPrix($source->getPrix()); // Copy constructeurs foreach ($source->getConstructeurs() as $constructeur) { $newMachine->getConstructeurs()->add($constructeur); } $this->entityManager->persist($newMachine); // Copy custom fields and values $this->cloneCustomFields($source, $newMachine); // Copy component links (preserving hierarchy with two-pass) $componentLinkMap = $this->cloneComponentLinks($source, $newMachine); // Copy piece links $pieceLinkMap = $this->clonePieceLinks($source, $newMachine, $componentLinkMap); // Copy product links $this->cloneProductLinks($source, $newMachine, $componentLinkMap, $pieceLinkMap); $this->entityManager->flush(); return $this->jsonResponse([ 'id' => $newMachine->getId(), 'name' => $newMachine->getName(), 'reference' => $newMachine->getReference(), 'siteId' => $site->getId(), 'clonedFrom' => $source->getId(), ]); } private function cloneCustomFields(Machine $source, Machine $target): void { foreach ($source->getCustomFields() as $cf) { $newCf = new CustomField(); $newCf->setName($cf->getName()); $newCf->setType($cf->getType()); $newCf->setRequired($cf->isRequired()); $newCf->setDefaultValue($cf->getDefaultValue()); $newCf->setOptions($cf->getOptions()); $newCf->setOrderIndex($cf->getOrderIndex()); $newCf->setMachine($target); $this->entityManager->persist($newCf); } foreach ($source->getCustomFieldValues() as $cfv) { $newValue = new CustomFieldValue(); $newValue->setMachine($target); $newValue->setCustomField($cfv->getCustomField()); $newValue->setValue($cfv->getValue()); $this->entityManager->persist($newValue); } } /** * @return array Map of old link ID to new link */ private function cloneComponentLinks(Machine $source, Machine $target): array { $sourceLinks = $this->machineComponentLinkRepository->findBy(['machine' => $source]); $linkMap = []; // First pass: create all links without parent relationships foreach ($sourceLinks as $link) { $newLink = new MachineComponentLink(); $newLink->setMachine($target); $newLink->setComposant($link->getComposant()); $newLink->setNameOverride($link->getNameOverride()); $newLink->setReferenceOverride($link->getReferenceOverride()); $newLink->setPrixOverride($link->getPrixOverride()); $this->entityManager->persist($newLink); $linkMap[$link->getId()] = $newLink; } // Second pass: set parent relationships foreach ($sourceLinks as $link) { $parent = $link->getParentLink(); if ($parent && isset($linkMap[$parent->getId()])) { $linkMap[$link->getId()]->setParentLink($linkMap[$parent->getId()]); } } return $linkMap; } /** * @param array $componentLinkMap * * @return array Map of old link ID to new link */ private function clonePieceLinks(Machine $source, Machine $target, array $componentLinkMap): array { $sourceLinks = $this->machinePieceLinkRepository->findBy(['machine' => $source]); $linkMap = []; foreach ($sourceLinks as $link) { $newLink = new MachinePieceLink(); $newLink->setMachine($target); $newLink->setPiece($link->getPiece()); $newLink->setNameOverride($link->getNameOverride()); $newLink->setReferenceOverride($link->getReferenceOverride()); $newLink->setPrixOverride($link->getPrixOverride()); $newLink->setQuantity($link->getQuantity()); $parent = $link->getParentLink(); if ($parent && isset($componentLinkMap[$parent->getId()])) { $newLink->setParentLink($componentLinkMap[$parent->getId()]); } $this->entityManager->persist($newLink); $linkMap[$link->getId()] = $newLink; } return $linkMap; } /** * @param array $componentLinkMap * @param array $pieceLinkMap */ private function cloneProductLinks( Machine $source, Machine $target, array $componentLinkMap, array $pieceLinkMap, ): void { $sourceLinks = $this->machineProductLinkRepository->findBy(['machine' => $source]); $linkMap = []; // First pass: create all links foreach ($sourceLinks as $link) { $newLink = new MachineProductLink(); $newLink->setMachine($target); $newLink->setProduct($link->getProduct()); $parentComponent = $link->getParentComponentLink(); if ($parentComponent && isset($componentLinkMap[$parentComponent->getId()])) { $newLink->setParentComponentLink($componentLinkMap[$parentComponent->getId()]); } $parentPiece = $link->getParentPieceLink(); if ($parentPiece && isset($pieceLinkMap[$parentPiece->getId()])) { $newLink->setParentPieceLink($pieceLinkMap[$parentPiece->getId()]); } $this->entityManager->persist($newLink); $linkMap[$link->getId()] = $newLink; } // Second pass: set parent product link relationships foreach ($sourceLinks as $link) { $parent = $link->getParentLink(); if ($parent && isset($linkMap[$parent->getId()])) { $linkMap[$link->getId()]->setParentLink($linkMap[$parent->getId()]); } } } }