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 fix migration (Version20260528090000) - 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), 'piece_product_slot', s.id, 'delete', json_build_object( 'id', s.id, 'pieceId', s.pieceid, 'note', 'Cleaned by FK cascade fix migration (Version20260528090000) - referenced piece no longer existed' ), NULL, NOW() FROM piece_product_slots s WHERE s.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), 'document', d.id, 'delete', json_build_object( 'id', d.id, 'name', d.name, 'filename', d.filename, 'pieceId', d.pieceid, 'note', 'Cleaned by FK cascade fix migration (Version20260528090000) - referenced piece no longer existed' ), NULL, NOW() FROM documents d WHERE d.pieceid IS NOT NULL AND d.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 fix migration (Version20260528090000) - 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); $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), 'piece_constructeur_link', l.id, 'delete', json_build_object( 'id', l.id, 'pieceId', l.pieceid, 'constructeurId', l.constructeurid, 'note', 'Cleaned by FK cascade fix migration (Version20260528090000) - referenced piece no longer existed' ), NULL, NOW() FROM piece_constructeur_links l WHERE l.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' UPDATE composant_piece_slots SET selectedpieceid = NULL WHERE selectedpieceid IS NOT NULL AND selectedpieceid NOT IN (SELECT id FROM pieces) SQL); $this->addSql(<<<'SQL' DELETE FROM piece_product_slots WHERE pieceid NOT IN (SELECT id FROM pieces) SQL); $this->addSql(<<<'SQL' DELETE FROM documents 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); $this->addSql(<<<'SQL' DELETE FROM piece_constructeur_links WHERE pieceid NOT IN (SELECT id FROM pieces) SQL); $this->addSql(<<<'SQL' DELETE FROM piece_products WHERE piece_id 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('composant_piece_slots', 'selectedpieceid'); $this->addSql(<<<'SQL' ALTER TABLE composant_piece_slots ADD CONSTRAINT fk_cps_selected_piece FOREIGN KEY (selectedpieceid) REFERENCES pieces(id) ON DELETE SET NULL SQL); $this->dropFksReferencingPieces('piece_product_slots', 'pieceid'); $this->addSql(<<<'SQL' ALTER TABLE piece_product_slots ADD CONSTRAINT fk_pps_piece FOREIGN KEY (pieceid) REFERENCES pieces(id) ON DELETE CASCADE SQL); $this->dropFksReferencingPieces('documents', 'pieceid'); $this->addSql(<<<'SQL' ALTER TABLE documents ADD CONSTRAINT fk_documents_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); $this->dropFksReferencingPieces('piece_constructeur_links', 'pieceid'); $this->addSql(<<<'SQL' ALTER TABLE piece_constructeur_links ADD CONSTRAINT fk_pcl_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 composant_piece_slots DROP CONSTRAINT IF EXISTS fk_cps_selected_piece'); $this->addSql('ALTER TABLE piece_product_slots DROP CONSTRAINT IF EXISTS fk_pps_piece'); $this->addSql('ALTER TABLE documents DROP CONSTRAINT IF EXISTS fk_documents_piece'); $this->addSql('ALTER TABLE custom_field_values DROP CONSTRAINT IF EXISTS fk_cfv_piece'); $this->addSql('ALTER TABLE piece_constructeur_links DROP CONSTRAINT IF EXISTS fk_pcl_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); } }