fix(data-integrity) : prevent data loss in clone, slots, conversion and custom fields

- Clone: CustomFieldValue now references cloned CustomField, not source
- Slots: validate piece type matches slot requirement + 404 on missing piece
- Conversion: check slot tables before allowing category conversion + clean orphan skeleton requirements
- CustomFieldValue: prevent creation of orphan CustomField without target entity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-23 17:15:05 +01:00
parent d5a43fc9bb
commit 043f6b1ce6
4 changed files with 81 additions and 13 deletions

View File

@@ -196,19 +196,16 @@ class CustomFieldValueController extends AbstractController
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);
// Try to find an existing custom field by name instead of creating an orphan
$existing = $this->customFieldRepository->findOneBy(['name' => $customFieldName]);
if ($existing instanceof CustomField) {
return $existing;
}
$this->entityManager->persist($customField);
return $customField;
return $this->json([
'success' => false,
'error' => sprintf('Custom field "%s" not found. Create it explicitly first.', $customFieldName),
], 404);
}
private function resolveTarget(array $payload): array|JsonResponse