fix(model-types) : nullify weak references on ModelType delete
Belt-and-suspenders against orphan refs when a ModelType is deleted: applicatively nullifies typeComposantId / typePieceId / typeProductId on every "ON DELETE SET NULL" relationship before the row is removed, in case the database FK cascade fails to fire. Observed in prod 2026-04-28: deletion of ModelType "Paliers" left an orphan in skeleton_subcomponent_requirements, surfacing as a 500 when API Platform tried to lazy-load the missing proxy. Co-Authored-By: RuFlo <ruv@ruv.net>
This commit is contained in:
57
src/EventSubscriber/ModelTypeReferenceCleanupSubscriber.php
Normal file
57
src/EventSubscriber/ModelTypeReferenceCleanupSubscriber.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use App\Entity\ModelType;
|
||||
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
|
||||
use Doctrine\ORM\Event\PreRemoveEventArgs;
|
||||
use Doctrine\ORM\Events;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Belt-and-suspenders cleanup of weak references to a ModelType before deletion:
|
||||
* runs the equivalent of every "ON DELETE SET NULL" cascade applicatively, in case
|
||||
* the database FK fails to fire (observed on prod in 2026-04 — the deletion of
|
||||
* ModelType "Paliers" left an orphan in skeleton_subcomponent_requirements).
|
||||
*/
|
||||
#[AsDoctrineListener(event: Events::preRemove)]
|
||||
final class ModelTypeReferenceCleanupSubscriber
|
||||
{
|
||||
/** @var list<array{0: string, 1: string}> */
|
||||
private const NULLABLE_REFERENCES = [
|
||||
['skeleton_subcomponent_requirements', 'typecomposantid'],
|
||||
['skeleton_piece_requirements', 'typepieceid'],
|
||||
['skeleton_product_requirements', 'typeproductid'],
|
||||
['composant_piece_slots', 'typepieceid'],
|
||||
['composant_product_slots', 'typeproductid'],
|
||||
['composant_subcomponent_slots', 'typecomposantid'],
|
||||
['piece_product_slots', 'typeproductid'],
|
||||
['machine_component_links', 'modeltypeid'],
|
||||
['machine_piece_links', 'modeltypeid'],
|
||||
['machine_product_links', 'modeltypeid'],
|
||||
];
|
||||
|
||||
public function preRemove(PreRemoveEventArgs $args): void
|
||||
{
|
||||
$entity = $args->getObject();
|
||||
if (!$entity instanceof ModelType) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = $entity->getId();
|
||||
if (!$id) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conn = $args->getObjectManager()->getConnection();
|
||||
foreach (self::NULLABLE_REFERENCES as [$table, $column]) {
|
||||
$conn->executeStatement(
|
||||
sprintf('UPDATE %s SET %s = NULL WHERE %s = ?', $table, $column, $column),
|
||||
[$id],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user