addSql(<<<'SQL' INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat) SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24), 'machine_piece_link', l.id, 'delete', json_build_object( 'id', l.id, 'machineId', l.machineid, 'pieceId', l.pieceid, 'note', 'Cleaned by FK cascade repair migration (Version20260529150000) - referenced piece no longer existed' ), NULL, NOW() FROM machine_piece_links l WHERE l.pieceid IS NOT NULL AND l.pieceid NOT IN (SELECT id FROM pieces) SQL); $this->addSql(<<<'SQL' INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat) SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24), 'custom_field_value', v.id, 'delete', json_build_object( 'id', v.id, 'pieceId', v.pieceid, 'note', 'Cleaned by FK cascade repair migration (Version20260529150000) - referenced piece no longer existed' ), NULL, NOW() FROM custom_field_values v WHERE v.pieceid IS NOT NULL AND v.pieceid NOT IN (SELECT id FROM pieces) SQL); // ========================================================================= // 2. Nettoyage des orphelins (avant ADD CONSTRAINT, sinon PG rejette). // ========================================================================= $this->addSql(<<<'SQL' DELETE FROM machine_piece_links WHERE pieceid IS NOT NULL AND pieceid NOT IN (SELECT id FROM pieces) SQL); $this->addSql(<<<'SQL' DELETE FROM custom_field_values WHERE pieceid IS NOT NULL AND pieceid NOT IN (SELECT id FROM pieces) SQL); // ========================================================================= // 3. Drop des éventuelles FK existantes vers `pieces` (quel que soit leur // nom historique), puis ADD CONSTRAINT avec le bon ON DELETE. // ========================================================================= $this->dropFksReferencingPieces('machine_piece_links', 'pieceid'); $this->addSql(<<<'SQL' ALTER TABLE machine_piece_links ADD CONSTRAINT fk_mpl_piece FOREIGN KEY (pieceid) REFERENCES pieces(id) ON DELETE CASCADE SQL); $this->dropFksReferencingPieces('custom_field_values', 'pieceid'); $this->addSql(<<<'SQL' ALTER TABLE custom_field_values ADD CONSTRAINT fk_cfv_piece FOREIGN KEY (pieceid) REFERENCES pieces(id) ON DELETE CASCADE SQL); } public function down(Schema $schema): void { $this->addSql('ALTER TABLE machine_piece_links DROP CONSTRAINT IF EXISTS fk_mpl_piece'); $this->addSql('ALTER TABLE custom_field_values DROP CONSTRAINT IF EXISTS fk_cfv_piece'); } /** * Drop every FK on $table.$column that references the `pieces` table, * regardless of its historic name. Idempotent. */ private function dropFksReferencingPieces(string $table, string $column): void { $sql = <<addSql($sql); } }