From 40b4b90ed8997979b9d13467ee1e8e9df8802786 Mon Sep 17 00:00:00 2001 From: r-dev Date: Mon, 12 Jan 2026 13:03:56 +0100 Subject: [PATCH] wip(api) : machine skeleton + type links --- Inventory_frontend | 2 +- commit-msg | 2 +- src/Controller/MachineSkeletonController.php | 68 +++++++++++++++++-- src/Entity/ModelType.php | 7 ++ src/Entity/TypeMachine.php | 8 ++- .../TypeMachineComponentRequirement.php | 10 +++ src/Entity/TypeMachinePieceRequirement.php | 10 +++ src/Entity/TypeMachineProductRequirement.php | 10 +++ 8 files changed, 105 insertions(+), 12 deletions(-) diff --git a/Inventory_frontend b/Inventory_frontend index e99f053..13e025f 160000 --- a/Inventory_frontend +++ b/Inventory_frontend @@ -1 +1 @@ -Subproject commit e99f0532332f1d33915885bc05a48f1809384297 +Subproject commit 13e025fe614e3c0fa605b7e75213b978bd16d085 diff --git a/commit-msg b/commit-msg index 901d964..c7eed6d 100644 --- a/commit-msg +++ b/commit-msg @@ -11,7 +11,7 @@ fi # Types autorisés (MINUSCULES uniquement) # Optionnel: scope => feat(auth) : ... -REGEX='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\([a-z0-9._-]+\))?\ :\ .+' +REGEX='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|wip)(\([a-z0-9._-]+\))?\ :\ .+' if [[ ! "$FIRST_LINE" =~ $REGEX ]]; then echo "❌ Message de commit invalide." diff --git a/src/Controller/MachineSkeletonController.php b/src/Controller/MachineSkeletonController.php index a85d55a..bf614fc 100644 --- a/src/Controller/MachineSkeletonController.php +++ b/src/Controller/MachineSkeletonController.php @@ -50,6 +50,26 @@ class MachineSkeletonController extends AbstractController ) { } + #[Route('/{id}/skeleton', name: 'machine_skeleton_get', methods: ['GET'])] + public function getSkeleton(string $id): JsonResponse + { + $machine = $this->machineRepository->find($id); + if (!$machine instanceof Machine) { + return $this->json(['success' => false, 'error' => 'Machine not found.'], 404); + } + + $componentLinks = $this->machineComponentLinkRepository->findBy(['machine' => $machine]); + $pieceLinks = $this->machinePieceLinkRepository->findBy(['machine' => $machine]); + $productLinks = $this->machineProductLinkRepository->findBy(['machine' => $machine]); + + return $this->json($this->normalizeMachineSkeletonResponse( + $machine, + $componentLinks, + $pieceLinks, + $productLinks + )); + } + #[Route('/{id}/skeleton', name: 'machine_skeleton_update', methods: ['PATCH'])] public function updateSkeleton(string $id, Request $request): JsonResponse { @@ -321,20 +341,17 @@ class MachineSkeletonController extends AbstractController $componentIndex = $this->indexNormalizedLinks($normalizedComponentLinks); $normalizedPieceLinks = $this->normalizePieceLinks($pieceLinks); + // Build component hierarchy foreach ($normalizedComponentLinks as &$link) { $parentId = $link['parentComponentLinkId'] ?? null; if ($parentId && isset($componentIndex[$parentId])) { - $componentIndex[$parentId]['childLinks'][] = $link; + $componentIndex[$parentId]['childLinks'][] = &$link; } } unset($link); - foreach ($normalizedPieceLinks as $pieceLink) { - $parentId = $pieceLink['parentComponentLinkId'] ?? null; - if ($parentId && isset($componentIndex[$parentId])) { - $componentIndex[$parentId]['pieceLinks'][] = $pieceLink; - } - } + // Add pieces to components recursively + $this->attachPiecesToComponents($componentIndex, $normalizedPieceLinks); return [ 'machine' => $this->normalizeMachine($machine), @@ -344,6 +361,43 @@ class MachineSkeletonController extends AbstractController ]; } + 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; + } + } + + // Recursively attach to child components + 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'] ?? $child['linkId'] ?? null; + if ($childId) { + foreach ($pieceLinks as $pieceLink) { + $parentId = $pieceLink['parentComponentLinkId'] ?? null; + if ($parentId === $childId) { + $child['pieceLinks'][] = $pieceLink; + } + } + } + + // Recursively process nested children + if (!empty($child['childLinks'])) { + $this->attachPiecesToChildComponents($child['childLinks'], $pieceLinks); + } + } + } + private function normalizeMachine(Machine $machine): array { return [ diff --git a/src/Entity/ModelType.php b/src/Entity/ModelType.php index 8c6739f..8b1f966 100644 --- a/src/Entity/ModelType.php +++ b/src/Entity/ModelType.php @@ -11,6 +11,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\Annotation\Groups; #[ORM\Entity(repositoryClass: ModelTypeRepository::class)] #[ORM\Table(name: 'model_types')] @@ -21,21 +22,27 @@ class ModelType { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] + #[Groups(['type_machine:read'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 120)] + #[Groups(['type_machine:read'])] private string $name; #[ORM\Column(type: Types::STRING, length: 60, unique: true)] + #[Groups(['type_machine:read'])] private string $code; #[ORM\Column(enumType: ModelCategory::class)] + #[Groups(['type_machine:read'])] private ModelCategory $category; #[ORM\Column(type: Types::TEXT, nullable: true)] + #[Groups(['type_machine:read'])] private ?string $notes = null; #[ORM\Column(type: Types::TEXT, nullable: true)] + #[Groups(['type_machine:read'])] private ?string $description = null; #[ORM\Column(type: Types::JSON, nullable: true, name: 'componentSkeleton')] diff --git a/src/Entity/TypeMachine.php b/src/Entity/TypeMachine.php index e45cd93..77f3648 100644 --- a/src/Entity/TypeMachine.php +++ b/src/Entity/TypeMachine.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Get; @@ -31,9 +32,7 @@ use Symfony\Component\Validator\Constraints as Assert; new Post(), new Put(), new Delete(), - ], - normalizationContext: ['groups' => ['type_machine:read']], - denormalizationContext: ['groups' => ['type_machine:write']] + ] )] class TypeMachine { @@ -99,18 +98,21 @@ class TypeMachine * @var Collection */ #[ORM\OneToMany(targetEntity: TypeMachineComponentRequirement::class, mappedBy: 'typeMachine', cascade: ['persist', 'remove'])] + #[ApiProperty(readableLink: true)] private Collection $componentRequirements; /** * @var Collection */ #[ORM\OneToMany(targetEntity: TypeMachinePieceRequirement::class, mappedBy: 'typeMachine', cascade: ['persist', 'remove'])] + #[ApiProperty(readableLink: true)] private Collection $pieceRequirements; /** * @var Collection */ #[ORM\OneToMany(targetEntity: TypeMachineProductRequirement::class, mappedBy: 'typeMachine', cascade: ['persist', 'remove'])] + #[ApiProperty(readableLink: true)] private Collection $productRequirements; public function __construct() diff --git a/src/Entity/TypeMachineComponentRequirement.php b/src/Entity/TypeMachineComponentRequirement.php index ac0dc12..5906707 100644 --- a/src/Entity/TypeMachineComponentRequirement.php +++ b/src/Entity/TypeMachineComponentRequirement.php @@ -4,12 +4,14 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use App\Repository\TypeMachineComponentRequirementRepository; 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\Annotation\Groups; #[ORM\Entity(repositoryClass: TypeMachineComponentRequirementRepository::class)] #[ORM\Table(name: 'type_machine_component_requirements')] @@ -19,24 +21,31 @@ class TypeMachineComponentRequirement { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] + #[Groups(['type_machine:read'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Groups(['type_machine:read'])] private ?string $label = null; #[ORM\Column(type: Types::INTEGER, options: ['default' => 1], name: 'minCount')] + #[Groups(['type_machine:read'])] private int $minCount = 1; #[ORM\Column(type: Types::INTEGER, nullable: true, name: 'maxCount')] + #[Groups(['type_machine:read'])] private ?int $maxCount = null; #[ORM\Column(type: Types::BOOLEAN, options: ['default' => true], name: 'required')] + #[Groups(['type_machine:read'])] private bool $required = true; #[ORM\Column(type: Types::BOOLEAN, options: ['default' => true], name: 'allowNewModels')] + #[Groups(['type_machine:read'])] private bool $allowNewModels = true; #[ORM\Column(type: Types::INTEGER, options: ['default' => 0], name: 'orderIndex')] + #[Groups(['type_machine:read'])] private int $orderIndex = 0; #[ORM\ManyToOne(targetEntity: TypeMachine::class, inversedBy: 'componentRequirements')] @@ -45,6 +54,7 @@ class TypeMachineComponentRequirement #[ORM\ManyToOne(targetEntity: ModelType::class, inversedBy: 'componentRequirements')] #[ORM\JoinColumn(name: 'typeComposantId', referencedColumnName: 'id', nullable: false)] + #[ApiProperty(readableLink: true)] private ModelType $typeComposant; /** diff --git a/src/Entity/TypeMachinePieceRequirement.php b/src/Entity/TypeMachinePieceRequirement.php index c8a0b6b..f771574 100644 --- a/src/Entity/TypeMachinePieceRequirement.php +++ b/src/Entity/TypeMachinePieceRequirement.php @@ -4,12 +4,14 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use App\Repository\TypeMachinePieceRequirementRepository; 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\Annotation\Groups; #[ORM\Entity(repositoryClass: TypeMachinePieceRequirementRepository::class)] #[ORM\Table(name: 'type_machine_piece_requirements')] @@ -19,24 +21,31 @@ class TypeMachinePieceRequirement { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] + #[Groups(['type_machine:read'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Groups(['type_machine:read'])] private ?string $label = null; #[ORM\Column(type: Types::INTEGER, options: ['default' => 0], name: 'minCount')] + #[Groups(['type_machine:read'])] private int $minCount = 0; #[ORM\Column(type: Types::INTEGER, nullable: true, name: 'maxCount')] + #[Groups(['type_machine:read'])] private ?int $maxCount = null; #[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])] + #[Groups(['type_machine:read'])] private bool $required = false; #[ORM\Column(type: Types::BOOLEAN, options: ['default' => true], name: 'allowNewModels')] + #[Groups(['type_machine:read'])] private bool $allowNewModels = true; #[ORM\Column(type: Types::INTEGER, options: ['default' => 0], name: 'orderIndex')] + #[Groups(['type_machine:read'])] private int $orderIndex = 0; #[ORM\ManyToOne(targetEntity: TypeMachine::class, inversedBy: 'pieceRequirements')] @@ -45,6 +54,7 @@ class TypeMachinePieceRequirement #[ORM\ManyToOne(targetEntity: ModelType::class, inversedBy: 'pieceRequirements')] #[ORM\JoinColumn(name: 'typePieceId', referencedColumnName: 'id', nullable: false)] + #[ApiProperty(readableLink: true)] private ModelType $typePiece; /** diff --git a/src/Entity/TypeMachineProductRequirement.php b/src/Entity/TypeMachineProductRequirement.php index 3261f4c..0665977 100644 --- a/src/Entity/TypeMachineProductRequirement.php +++ b/src/Entity/TypeMachineProductRequirement.php @@ -4,12 +4,14 @@ declare(strict_types=1); namespace App\Entity; +use ApiPlatform\Metadata\ApiProperty; use ApiPlatform\Metadata\ApiResource; use App\Repository\TypeMachineProductRequirementRepository; 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\Annotation\Groups; #[ORM\Entity(repositoryClass: TypeMachineProductRequirementRepository::class)] #[ORM\Table(name: 'type_machine_product_requirements')] @@ -19,24 +21,31 @@ class TypeMachineProductRequirement { #[ORM\Id] #[ORM\Column(type: Types::STRING, length: 36)] + #[Groups(['type_machine:read'])] private ?string $id = null; #[ORM\Column(type: Types::STRING, length: 255, nullable: true)] + #[Groups(['type_machine:read'])] private ?string $label = null; #[ORM\Column(type: Types::INTEGER, options: ['default' => 0], name: 'minCount')] + #[Groups(['type_machine:read'])] private int $minCount = 0; #[ORM\Column(type: Types::INTEGER, nullable: true, name: 'maxCount')] + #[Groups(['type_machine:read'])] private ?int $maxCount = null; #[ORM\Column(type: Types::BOOLEAN, options: ['default' => false])] + #[Groups(['type_machine:read'])] private bool $required = false; #[ORM\Column(type: Types::BOOLEAN, options: ['default' => true], name: 'allowNewModels')] + #[Groups(['type_machine:read'])] private bool $allowNewModels = true; #[ORM\Column(type: Types::INTEGER, options: ['default' => 0], name: 'orderIndex')] + #[Groups(['type_machine:read'])] private int $orderIndex = 0; #[ORM\ManyToOne(targetEntity: TypeMachine::class, inversedBy: 'productRequirements')] @@ -45,6 +54,7 @@ class TypeMachineProductRequirement #[ORM\ManyToOne(targetEntity: ModelType::class, inversedBy: 'productRequirements')] #[ORM\JoinColumn(name: 'typeProductId', referencedColumnName: 'id', nullable: false)] + #[ApiProperty(readableLink: true)] private ModelType $typeProduct; /**