Backend service and controller for converting piece categories to component categories (and vice-versa). Uses raw SQL in a transaction to preserve IDs and transfer all related data (documents, custom fields, constructeurs). Includes php-cs-fixer formatting pass on existing controllers/entities. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
295 lines
10 KiB
PHP
295 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Entity\CustomField;
|
|
use App\Entity\CustomFieldValue;
|
|
use App\Repository\ComposantRepository;
|
|
use App\Repository\CustomFieldRepository;
|
|
use App\Repository\CustomFieldValueRepository;
|
|
use App\Repository\MachineRepository;
|
|
use App\Repository\PieceRepository;
|
|
use App\Repository\ProductRepository;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
|
|
#[Route('/api/custom-fields/values')]
|
|
class CustomFieldValueController extends AbstractController
|
|
{
|
|
public function __construct(
|
|
private readonly EntityManagerInterface $entityManager,
|
|
private readonly CustomFieldRepository $customFieldRepository,
|
|
private readonly CustomFieldValueRepository $customFieldValueRepository,
|
|
private readonly MachineRepository $machineRepository,
|
|
private readonly ComposantRepository $composantRepository,
|
|
private readonly PieceRepository $pieceRepository,
|
|
private readonly ProductRepository $productRepository,
|
|
) {}
|
|
|
|
#[Route('', name: 'custom_field_values_create', methods: ['POST'])]
|
|
public function create(Request $request): JsonResponse
|
|
{
|
|
$payload = $this->decodePayload($request);
|
|
if ($payload instanceof JsonResponse) {
|
|
return $payload;
|
|
}
|
|
|
|
$customField = $this->resolveCustomField($payload);
|
|
if ($customField instanceof JsonResponse) {
|
|
return $customField;
|
|
}
|
|
|
|
$target = $this->resolveTarget($payload);
|
|
if ($target instanceof JsonResponse) {
|
|
return $target;
|
|
}
|
|
|
|
$value = new CustomFieldValue();
|
|
$value->setCustomField($customField);
|
|
$value->setValue((string) ($payload['value'] ?? ''));
|
|
$this->applyTarget($value, $target['type'], $target['entity']);
|
|
|
|
$this->entityManager->persist($value);
|
|
$this->entityManager->flush();
|
|
|
|
return $this->json($this->normalizeCustomFieldValue($value));
|
|
}
|
|
|
|
#[Route('/upsert', name: 'custom_field_values_upsert', methods: ['POST'])]
|
|
public function upsert(Request $request): JsonResponse
|
|
{
|
|
$payload = $this->decodePayload($request);
|
|
if ($payload instanceof JsonResponse) {
|
|
return $payload;
|
|
}
|
|
|
|
$customField = $this->resolveCustomField($payload);
|
|
if ($customField instanceof JsonResponse) {
|
|
return $customField;
|
|
}
|
|
|
|
$target = $this->resolveTarget($payload);
|
|
if ($target instanceof JsonResponse) {
|
|
return $target;
|
|
}
|
|
|
|
$existing = $this->customFieldValueRepository->findOneBy([
|
|
'customField' => $customField,
|
|
$target['type'] => $target['entity'],
|
|
]);
|
|
|
|
if ($existing instanceof CustomFieldValue) {
|
|
$existing->setValue((string) ($payload['value'] ?? ''));
|
|
$this->entityManager->flush();
|
|
|
|
return $this->json($this->normalizeCustomFieldValue($existing));
|
|
}
|
|
|
|
$value = new CustomFieldValue();
|
|
$value->setCustomField($customField);
|
|
$value->setValue((string) ($payload['value'] ?? ''));
|
|
$this->applyTarget($value, $target['type'], $target['entity']);
|
|
|
|
$this->entityManager->persist($value);
|
|
$this->entityManager->flush();
|
|
|
|
return $this->json($this->normalizeCustomFieldValue($value));
|
|
}
|
|
|
|
#[Route('/{entityType}/{entityId}', name: 'custom_field_values_list', methods: ['GET'])]
|
|
public function listByEntity(string $entityType, string $entityId): JsonResponse
|
|
{
|
|
$target = $this->resolveTarget([
|
|
'entityType' => $entityType,
|
|
'entityId' => $entityId,
|
|
]);
|
|
|
|
if ($target instanceof JsonResponse) {
|
|
return $target;
|
|
}
|
|
|
|
$values = $this->customFieldValueRepository->findBy([
|
|
$target['type'] => $target['entity'],
|
|
]);
|
|
|
|
return $this->json(array_map(
|
|
fn (CustomFieldValue $value) => $this->normalizeCustomFieldValue($value),
|
|
$values
|
|
));
|
|
}
|
|
|
|
#[Route('/{id}', name: 'custom_field_values_update', methods: ['PATCH'])]
|
|
public function update(string $id, Request $request): JsonResponse
|
|
{
|
|
$value = $this->customFieldValueRepository->find($id);
|
|
if (!$value instanceof CustomFieldValue) {
|
|
return $this->json(['success' => false, 'error' => 'Custom field value not found.'], 404);
|
|
}
|
|
|
|
$payload = $this->decodePayload($request);
|
|
if ($payload instanceof JsonResponse) {
|
|
return $payload;
|
|
}
|
|
|
|
if (array_key_exists('value', $payload)) {
|
|
$value->setValue((string) $payload['value']);
|
|
}
|
|
|
|
$this->entityManager->flush();
|
|
|
|
return $this->json($this->normalizeCustomFieldValue($value));
|
|
}
|
|
|
|
#[Route('/{id}', name: 'custom_field_values_delete', methods: ['DELETE'])]
|
|
public function delete(string $id): JsonResponse
|
|
{
|
|
$value = $this->customFieldValueRepository->find($id);
|
|
if (!$value instanceof CustomFieldValue) {
|
|
return $this->json(['success' => false, 'error' => 'Custom field value not found.'], 404);
|
|
}
|
|
|
|
$this->entityManager->remove($value);
|
|
$this->entityManager->flush();
|
|
|
|
return $this->json(['success' => true]);
|
|
}
|
|
|
|
private function decodePayload(Request $request): array|JsonResponse
|
|
{
|
|
$payload = json_decode($request->getContent(), true);
|
|
if (!is_array($payload)) {
|
|
return $this->json(['success' => false, 'error' => 'Invalid JSON payload.'], 400);
|
|
}
|
|
|
|
return $payload;
|
|
}
|
|
|
|
private function resolveCustomField(array $payload): CustomField|JsonResponse
|
|
{
|
|
$customFieldId = isset($payload['customFieldId']) ? trim((string) $payload['customFieldId']) : '';
|
|
if ('' !== $customFieldId) {
|
|
$customField = $this->customFieldRepository->find($customFieldId);
|
|
if ($customField instanceof CustomField) {
|
|
return $customField;
|
|
}
|
|
|
|
return $this->json(['success' => false, 'error' => 'Custom field not found.'], 404);
|
|
}
|
|
|
|
$customFieldName = isset($payload['customFieldName']) ? trim((string) $payload['customFieldName']) : '';
|
|
if ('' === $customFieldName) {
|
|
return $this->json(['success' => false, 'error' => 'customFieldId or customFieldName is required.'], 400);
|
|
}
|
|
|
|
$customField = new CustomField();
|
|
$customField->setName($customFieldName);
|
|
$customField->setType((string) ($payload['customFieldType'] ?? 'text'));
|
|
$customField->setRequired((bool) ($payload['customFieldRequired'] ?? false));
|
|
|
|
$options = $payload['customFieldOptions'] ?? null;
|
|
if (is_array($options)) {
|
|
$customField->setOptions($options);
|
|
}
|
|
|
|
$this->entityManager->persist($customField);
|
|
|
|
return $customField;
|
|
}
|
|
|
|
private function resolveTarget(array $payload): array|JsonResponse
|
|
{
|
|
$entityType = isset($payload['entityType']) ? strtolower((string) $payload['entityType']) : '';
|
|
$entityId = isset($payload['entityId']) ? trim((string) $payload['entityId']) : '';
|
|
|
|
if ('' === $entityType || '' === $entityId) {
|
|
foreach (['machine', 'composant', 'piece', 'product'] as $candidate) {
|
|
$key = $candidate.'Id';
|
|
if (!isset($payload[$key])) {
|
|
continue;
|
|
}
|
|
$entityType = $candidate;
|
|
$entityId = trim((string) $payload[$key]);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ('' === $entityType || '' === $entityId) {
|
|
return $this->json(['success' => false, 'error' => 'Entity target is missing.'], 400);
|
|
}
|
|
|
|
return match ($entityType) {
|
|
'machine' => $this->resolveEntity('machine', $entityId, $this->machineRepository),
|
|
'composant' => $this->resolveEntity('composant', $entityId, $this->composantRepository),
|
|
'piece' => $this->resolveEntity('piece', $entityId, $this->pieceRepository),
|
|
'product' => $this->resolveEntity('product', $entityId, $this->productRepository),
|
|
default => $this->json(['success' => false, 'error' => 'Unsupported entity type.'], 400),
|
|
};
|
|
}
|
|
|
|
private function resolveEntity(string $type, string $id, $repository): array|JsonResponse
|
|
{
|
|
$entity = $repository->find($id);
|
|
if (!$entity) {
|
|
return $this->json(['success' => false, 'error' => sprintf('%s not found.', $type)], 404);
|
|
}
|
|
|
|
return ['type' => $type, 'entity' => $entity];
|
|
}
|
|
|
|
private function applyTarget(CustomFieldValue $value, string $type, object $entity): void
|
|
{
|
|
switch ($type) {
|
|
case 'machine':
|
|
$value->setMachine($entity);
|
|
|
|
break;
|
|
|
|
case 'composant':
|
|
$value->setComposant($entity);
|
|
|
|
break;
|
|
|
|
case 'piece':
|
|
$value->setPiece($entity);
|
|
|
|
break;
|
|
|
|
case 'product':
|
|
$value->setProduct($entity);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
private function normalizeCustomFieldValue(CustomFieldValue $value): array
|
|
{
|
|
$customField = $value->getCustomField();
|
|
|
|
return [
|
|
'id' => $value->getId(),
|
|
'value' => $value->getValue(),
|
|
'customFieldId' => $customField->getId(),
|
|
'customField' => [
|
|
'id' => $customField->getId(),
|
|
'name' => $customField->getName(),
|
|
'type' => $customField->getType(),
|
|
'required' => $customField->isRequired(),
|
|
'options' => $customField->getOptions(),
|
|
'orderIndex' => $customField->getOrderIndex(),
|
|
],
|
|
'machineId' => $value->getMachine()?->getId(),
|
|
'composantId' => $value->getComposant()?->getId(),
|
|
'pieceId' => $value->getPiece()?->getId(),
|
|
'productId' => $value->getProduct()?->getId(),
|
|
'createdAt' => $value->getCreatedAt()->format(DATE_ATOM),
|
|
'updatedAt' => $value->getUpdatedAt()->format(DATE_ATOM),
|
|
];
|
|
}
|
|
}
|