machineRepository->find($machineId); if (!$machine instanceof Machine) { $this->mcpError('not_found', "Machine not found: {$machineId}"); } $componentLinks = $this->machineComponentLinkRepository->findBy(['machine' => $machine]); $pieceLinks = $this->machinePieceLinkRepository->findBy(['machine' => $machine]); $productLinks = $this->machineProductLinkRepository->findBy(['machine' => $machine]); return $this->jsonResponse($this->normalizeStructureResponse( $machine, $componentLinks, $pieceLinks, $productLinks, )); } /** * @param MachineComponentLink[] $componentLinks * @param MachinePieceLink[] $pieceLinks * @param MachineProductLink[] $productLinks */ private function normalizeStructureResponse( Machine $machine, array $componentLinks, array $pieceLinks, array $productLinks, ): array { $normalizedComponentLinks = $this->normalizeComponentLinks($componentLinks); $componentIndex = $this->indexById($normalizedComponentLinks); $normalizedPieceLinks = $this->normalizePieceLinks($pieceLinks); $childIds = []; foreach ($normalizedComponentLinks as $link) { $parentId = $link['parentComponentLinkId'] ?? null; if ($parentId && isset($componentIndex[$parentId])) { $componentIndex[$parentId]['childLinks'][] = $link; $childIds[$link['id']] = true; } } $this->attachPiecesToComponents($componentIndex, $normalizedPieceLinks); $rootComponents = array_filter( $componentIndex, static fn (array $link) => !isset($childIds[$link['id']]), ); return [ 'machine' => $this->normalizeMachine($machine), 'componentLinks' => array_values($rootComponents), 'pieceLinks' => $normalizedPieceLinks, 'productLinks' => $this->normalizeProductLinks($productLinks), ]; } private function attachPiecesToComponents(array &$componentIndex, array $pieceLinks): void { foreach ($pieceLinks as $pieceLink) { $parentId = $pieceLink['parentComponentLinkId'] ?? null; if ($parentId && isset($componentIndex[$parentId])) { $componentIndex[$parentId]['pieceLinks'][] = $pieceLink; } } foreach ($componentIndex as &$component) { if (!empty($component['childLinks'])) { $this->attachPiecesToChildComponents($component['childLinks'], $pieceLinks); } } } private function attachPiecesToChildComponents(array &$childLinks, array $pieceLinks): void { foreach ($childLinks as &$child) { $childId = $child['id'] ?? null; if ($childId) { foreach ($pieceLinks as $pieceLink) { if (($pieceLink['parentComponentLinkId'] ?? null) === $childId) { $child['pieceLinks'][] = $pieceLink; } } } if (!empty($child['childLinks'])) { $this->attachPiecesToChildComponents($child['childLinks'], $pieceLinks); } } } private function normalizeMachine(Machine $machine): array { $site = $machine->getSite(); return [ 'id' => $machine->getId(), 'name' => $machine->getName(), 'reference' => $machine->getReference(), 'prix' => $machine->getPrix(), 'siteId' => $site->getId(), 'site' => [ 'id' => $site->getId(), 'name' => $site->getName(), ], 'constructeurs' => $this->normalizeConstructeurs($machine->getConstructeurs()), 'customFields' => $this->normalizeCustomFields($machine->getCustomFields()), 'customFieldValues' => $this->normalizeCustomFieldValues($machine->getCustomFieldValues()), ]; } /** * @param MachineComponentLink[] $links */ private function normalizeComponentLinks(array $links): array { return array_map(function (MachineComponentLink $link): array { $composant = $link->getComposant(); $parentLink = $link->getParentLink(); return [ 'id' => $link->getId(), 'linkId' => $link->getId(), 'machineId' => $link->getMachine()->getId(), 'composantId' => $composant->getId(), 'composant' => $this->normalizeComposant($composant), 'parentLinkId' => $parentLink?->getId(), 'parentComponentLinkId' => $parentLink?->getId(), 'parentComponentId' => $parentLink?->getComposant()->getId(), 'overrides' => $this->normalizeOverrides($link), 'childLinks' => [], 'pieceLinks' => [], ]; }, $links); } /** * @param MachinePieceLink[] $links */ private function normalizePieceLinks(array $links): array { return array_map(function (MachinePieceLink $link): array { $piece = $link->getPiece(); $parentLink = $link->getParentLink(); return [ 'id' => $link->getId(), 'linkId' => $link->getId(), 'machineId' => $link->getMachine()->getId(), 'pieceId' => $piece->getId(), 'piece' => $this->normalizePiece($piece), 'parentLinkId' => $parentLink?->getId(), 'parentComponentLinkId' => $parentLink?->getId(), 'parentComponentId' => $parentLink?->getComposant()->getId(), 'overrides' => $this->normalizeOverrides($link), 'quantity' => $this->resolvePieceQuantity($link), ]; }, $links); } private function resolvePieceQuantity(MachinePieceLink $link): int { $parentLink = $link->getParentLink(); if (!$parentLink) { return $link->getQuantity(); } $composant = $parentLink->getComposant(); $piece = $link->getPiece(); foreach ($composant->getPieceSlots() as $slot) { if ($slot->getSelectedPiece()?->getId() === $piece->getId()) { return $slot->getQuantity(); } } return $link->getQuantity(); } /** * @param MachineProductLink[] $links */ private function normalizeProductLinks(array $links): array { return array_map(function (MachineProductLink $link): array { $product = $link->getProduct(); return [ 'id' => $link->getId(), 'linkId' => $link->getId(), 'machineId' => $link->getMachine()->getId(), 'productId' => $product->getId(), 'product' => $this->normalizeProduct($product), 'parentLinkId' => $link->getParentLink()?->getId(), 'parentComponentLinkId' => $link->getParentComponentLink()?->getId(), 'parentPieceLinkId' => $link->getParentPieceLink()?->getId(), ]; }, $links); } private function normalizeComposant(Composant $composant): array { $type = $composant->getTypeComposant(); return [ 'id' => $composant->getId(), 'name' => $composant->getName(), 'reference' => $composant->getReference(), 'prix' => $composant->getPrix(), 'typeComposantId' => $type?->getId(), 'typeComposant' => $this->normalizeModelType($type), 'productId' => $composant->getProduct()?->getId(), 'product' => $composant->getProduct() ? $this->normalizeProduct($composant->getProduct()) : null, 'structure' => $this->buildStructureFromSlots($composant), 'constructeurs' => $this->normalizeConstructeurs($composant->getConstructeurs()), 'customFields' => $type ? $this->normalizeCustomFieldDefinitions($type->getComponentCustomFields()) : [], 'customFieldValues' => $this->normalizeCustomFieldValues($composant->getCustomFieldValues()), ]; } private function buildStructureFromSlots(Composant $composant): array { $pieces = []; foreach ($composant->getPieceSlots() as $slot) { $pieceData = [ 'slotId' => $slot->getId(), 'typePieceId' => $slot->getTypePiece()?->getId(), 'quantity' => $slot->getQuantity(), 'selectedPieceId' => $slot->getSelectedPiece()?->getId(), ]; if ($slot->getSelectedPiece()) { $pieceData['resolvedPiece'] = $this->normalizePiece($slot->getSelectedPiece()); } $pieces[] = $pieceData; } $subcomponents = []; foreach ($composant->getSubcomponentSlots() as $slot) { $subcomponents[] = [ 'alias' => $slot->getAlias(), 'familyCode' => $slot->getFamilyCode(), 'typeComposantId' => $slot->getTypeComposant()?->getId(), 'selectedComponentId' => $slot->getSelectedComposant()?->getId(), ]; } $products = []; foreach ($composant->getProductSlots() as $slot) { $products[] = [ 'typeProductId' => $slot->getTypeProduct()?->getId(), 'familyCode' => $slot->getFamilyCode(), 'selectedProductId' => $slot->getSelectedProduct()?->getId(), ]; } return [ 'pieces' => $pieces, 'subcomponents' => $subcomponents, 'products' => $products, ]; } private function normalizePiece(Piece $piece): array { $type = $piece->getTypePiece(); return [ 'id' => $piece->getId(), 'name' => $piece->getName(), 'reference' => $piece->getReference(), 'prix' => $piece->getPrix(), 'typePieceId' => $type?->getId(), 'typePiece' => $this->normalizeModelType($type), 'productId' => $piece->getProduct()?->getId(), 'product' => $piece->getProduct() ? $this->normalizeProduct($piece->getProduct()) : null, 'constructeurs' => $this->normalizeConstructeurs($piece->getConstructeurs()), 'customFields' => $type ? $this->normalizeCustomFieldDefinitions($type->getPieceCustomFields()) : [], 'customFieldValues' => $this->normalizeCustomFieldValues($piece->getCustomFieldValues()), ]; } private function normalizeProduct(Product $product): array { $type = $product->getTypeProduct(); return [ 'id' => $product->getId(), 'name' => $product->getName(), 'reference' => $product->getReference(), 'supplierPrice' => $product->getSupplierPrice(), 'typeProductId' => $type?->getId(), 'typeProduct' => $this->normalizeModelType($type), 'constructeurs' => $this->normalizeConstructeurs($product->getConstructeurs()), 'customFields' => $type ? $this->normalizeCustomFieldDefinitions($type->getProductCustomFields()) : [], 'customFieldValues' => $this->normalizeCustomFieldValues($product->getCustomFieldValues()), ]; } private function normalizeModelType(?ModelType $type): ?array { if (!$type instanceof ModelType) { return null; } return [ 'id' => $type->getId(), 'name' => $type->getName(), 'code' => $type->getCode(), 'category' => $type->getCategory()->value, 'structure' => $type->getStructure(), ]; } private function normalizeConstructeurs(Collection $constructeurs): array { $items = []; foreach ($constructeurs as $constructeur) { $items[] = [ 'id' => $constructeur->getId(), 'name' => $constructeur->getName(), 'email' => $constructeur->getEmail(), 'phone' => $constructeur->getPhone(), ]; } return $items; } private function normalizeCustomFields(Collection $customFields): array { $items = []; foreach ($customFields as $customField) { if (!$customField instanceof CustomField) { continue; } $items[] = [ 'id' => $customField->getId(), 'name' => $customField->getName(), 'type' => $customField->getType(), 'required' => $customField->isRequired(), 'options' => $customField->getOptions(), 'defaultValue' => $customField->getDefaultValue(), 'orderIndex' => $customField->getOrderIndex(), ]; } return $items; } private function normalizeCustomFieldDefinitions(Collection $customFields): array { $items = []; foreach ($customFields as $cf) { if (!$cf instanceof CustomField) { continue; } $items[] = [ 'id' => $cf->getId(), 'name' => $cf->getName(), 'type' => $cf->getType(), 'required' => $cf->isRequired(), 'options' => $cf->getOptions(), 'defaultValue' => $cf->getDefaultValue(), 'orderIndex' => $cf->getOrderIndex(), ]; } usort($items, static fn (array $a, array $b) => $a['orderIndex'] <=> $b['orderIndex']); return $items; } private function normalizeCustomFieldValues(Collection $customFieldValues): array { $items = []; foreach ($customFieldValues as $cfv) { if (!$cfv instanceof CustomFieldValue) { continue; } $cf = $cfv->getCustomField(); $items[] = [ 'id' => $cfv->getId(), 'value' => $cfv->getValue(), 'customField' => [ 'id' => $cf->getId(), 'name' => $cf->getName(), 'type' => $cf->getType(), 'required' => $cf->isRequired(), 'options' => $cf->getOptions(), 'defaultValue' => $cf->getDefaultValue(), 'orderIndex' => $cf->getOrderIndex(), ], ]; } return $items; } private function normalizeOverrides(object $link): ?array { $name = method_exists($link, 'getNameOverride') ? $link->getNameOverride() : null; $reference = method_exists($link, 'getReferenceOverride') ? $link->getReferenceOverride() : null; $prix = method_exists($link, 'getPrixOverride') ? $link->getPrixOverride() : null; if (null === $name && null === $reference && null === $prix) { return null; } return [ 'name' => $name, 'reference' => $reference, 'prix' => $prix, ]; } private function indexById(array $links): array { $indexed = []; foreach ($links as $link) { if (is_array($link) && isset($link['id'])) { $indexed[$link['id']] = $link; } } return $indexed; } }