Compare commits

...

4 Commits

Author SHA1 Message Date
gitea-actions 2b318ce5d6 chore : bump version to v1.9.42
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 40s
2026-05-28 15:09:49 +00:00
Matthieu c10ab08803 fix(custom-fields) : forcer initializeObject pour vraiment charger le proxy
Auto Tag Develop / tag (push) Successful in 10s
Le helper ensureCustomFieldExists (commit af13dc0) appelait $cf->getId()
pour déclencher l'init du proxy, mais sur un proxy Doctrine getId() retourne
directement l'identifiant stocké dans le proxy (la clé utilisée pour le
construire) sans appeler __load(). L'EntityNotFoundException n'était donc
jamais levée dans le helper et le crash sortait quand même sur getName()
ligne 973.

Remplacement par EntityManager::initializeObject() qui appelle __load() et
propage bien l'exception. Même correction appliquée à ensurePieceExists()
dans les deux contrôleurs (le bug y était masqué par la migration FK
CASCADE/SET NULL livrée dans le commit 003e419).
2026-05-28 17:09:34 +02:00
gitea-actions 85d4726415 chore : bump version to v1.9.41
Auto Tag Develop / tag (push) Successful in 8s
Build & Push Docker Image / build (push) Successful in 38s
2026-05-28 14:49:28 +00:00
Matthieu af13dc0237 fix(custom-fields) : empêche EntityNotFoundException sur CustomField orphelin
Auto Tag Develop / tag (push) Successful in 9s
Même pattern que la fix Piece (003e419) : helper ensureCustomFieldExists()
qui force l'init du proxy lazy et catch EntityNotFoundException dans
MachineStructureController::normalizeCustomFieldValues() et
CustomFieldValueController::normalizeCustomFieldValue(). Les CFV pointant
vers un CustomField supprimé sont silencieusement skippés au lieu de
crasher la vue machine entière.
2026-05-28 16:48:58 +02:00
3 changed files with 57 additions and 12 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
parameters: parameters:
app.version: '1.9.40' app.version: '1.9.42'
+28 -7
View File
@@ -298,8 +298,9 @@ class CustomFieldValueController extends AbstractController
/** /**
* Returns the Piece if its underlying row still exists in DB, otherwise null. * Returns the Piece if its underlying row still exists in DB, otherwise null.
* Forces a lazy proxy to initialize via getId() and swallows EntityNotFoundException * getId() on a Doctrine proxy does NOT trigger __load(), so we force the proxy
* so an orphan link to a deleted piece doesn't crash custom-field value writes. * to initialize explicitly to handle orphan links here instead of crashing on
* the first real getter.
*/ */
private function ensurePieceExists(?Piece $piece): ?Piece private function ensurePieceExists(?Piece $piece): ?Piece
{ {
@@ -307,7 +308,7 @@ class CustomFieldValueController extends AbstractController
return null; return null;
} }
try { try {
$piece->getId(); $this->entityManager->initializeObject($piece);
return $piece; return $piece;
} catch (EntityNotFoundException) { } catch (EntityNotFoundException) {
@@ -315,22 +316,42 @@ class CustomFieldValueController extends AbstractController
} }
} }
/**
* getId() on a Doctrine proxy returns the identifier without triggering __load(),
* so it never raises EntityNotFoundException even if the row is gone. Force the
* proxy to initialize explicitly so an orphan CFV is handled here instead of
* crashing on the first real getter.
*/
private function ensureCustomFieldExists(?CustomField $cf): ?CustomField
{
if (null === $cf) {
return null;
}
try {
$this->entityManager->initializeObject($cf);
return $cf;
} catch (EntityNotFoundException) {
return null;
}
}
private function normalizeCustomFieldValue(CustomFieldValue $value): array private function normalizeCustomFieldValue(CustomFieldValue $value): array
{ {
$customField = $value->getCustomField(); $customField = $this->ensureCustomFieldExists($value->getCustomField());
return [ return [
'id' => $value->getId(), 'id' => $value->getId(),
'value' => $value->getValue(), 'value' => $value->getValue(),
'customFieldId' => $customField->getId(), 'customFieldId' => $customField?->getId(),
'customField' => [ 'customField' => $customField ? [
'id' => $customField->getId(), 'id' => $customField->getId(),
'name' => $customField->getName(), 'name' => $customField->getName(),
'type' => $customField->getType(), 'type' => $customField->getType(),
'required' => $customField->isRequired(), 'required' => $customField->isRequired(),
'options' => $customField->getOptions(), 'options' => $customField->getOptions(),
'orderIndex' => $customField->getOrderIndex(), 'orderIndex' => $customField->getOrderIndex(),
], ] : null,
'machineId' => $value->getMachine()?->getId(), 'machineId' => $value->getMachine()?->getId(),
'composantId' => $value->getComposant()?->getId(), 'composantId' => $value->getComposant()?->getId(),
'pieceId' => $value->getPiece()?->getId(), 'pieceId' => $value->getPiece()?->getId(),
+28 -4
View File
@@ -815,8 +815,9 @@ class MachineStructureController extends AbstractController
/** /**
* Returns the Piece if its underlying row still exists in DB, otherwise null. * Returns the Piece if its underlying row still exists in DB, otherwise null.
* Forces a lazy proxy to initialize via getId() and swallows EntityNotFoundException * getId() on a Doctrine proxy does NOT trigger __load() (the id is the key used
* so a stale FK (orphan link to a deleted piece) doesn't crash the whole machine view. * to build the proxy), so we force initialization via initializeObject() to
* surface a stale FK here instead of crashing on the first real getter.
*/ */
private function ensurePieceExists(?Piece $piece): ?Piece private function ensurePieceExists(?Piece $piece): ?Piece
{ {
@@ -824,7 +825,7 @@ class MachineStructureController extends AbstractController
return null; return null;
} }
try { try {
$piece->getId(); $this->entityManager->initializeObject($piece);
return $piece; return $piece;
} catch (EntityNotFoundException) { } catch (EntityNotFoundException) {
@@ -832,6 +833,26 @@ class MachineStructureController extends AbstractController
} }
} }
/**
* Returns the CustomField if its underlying row still exists, otherwise null.
* getId() on a Doctrine proxy does NOT trigger __load() — the id is the key used
* to build the proxy. We force initialization explicitly so a stale FK to a
* deleted CustomField surfaces here instead of crashing on getName() later.
*/
private function ensureCustomFieldExists(?CustomField $cf): ?CustomField
{
if (null === $cf) {
return null;
}
try {
$this->entityManager->initializeObject($cf);
return $cf;
} catch (EntityNotFoundException) {
return null;
}
}
private function normalizePiece(Piece $piece): array private function normalizePiece(Piece $piece): array
{ {
$type = $piece->getTypePiece(); $type = $piece->getTypePiece();
@@ -942,7 +963,10 @@ class MachineStructureController extends AbstractController
if (!$cfv instanceof CustomFieldValue) { if (!$cfv instanceof CustomFieldValue) {
continue; continue;
} }
$cf = $cfv->getCustomField(); $cf = $this->ensureCustomFieldExists($cfv->getCustomField());
if (null === $cf) {
continue;
}
$items[] = [ $items[] = [
'id' => $cfv->getId(), 'id' => $cfv->getId(),
'value' => $cfv->getValue(), 'value' => $cfv->getValue(),