prepare( "SELECT source_id,target_id FROM asm3_import_links WHERE source_sha256=:h AND entity_type='animal'", ); $q->execute([':h' => $hash]); foreach ($q as $r) { $links[(int) $r['source_id']] = (int) $r['target_id']; } $animals = []; foreach ($src['animal'] ?? [] as $a) { $animals[(string) $a['ACCEPTANCENUMBER']][] = (int) $a['ID']; } $result = [ 'mode' => $apply ? 'apply' : 'dry-run', 'read_only' => !$apply, 'litters' => count($src['animallitter'] ?? []), 'would_import' => 0, 'unresolved' => 0, 'created' => 0, 'kittens_linked' => 0, 'backup' => null, ]; foreach ($src['animallitter'] ?? [] as $l) { $parent = (int) $l['PARENTANIMALID']; $kids = array_filter($animals[(string) $l['ACCEPTANCENUMBER']] ?? [], fn($id) => $id !== $parent); if (!isset($links[$parent]) || array_filter($kids, fn($id) => !isset($links[$id]))) { $result['unresolved']++; continue; } $result['would_import']++; } if (!$apply) { return $result; } if ($createBackup) { $b = BackupService::create('pre-asm3-import'); $result['backup'] = $b['name'] ?? null; } $owns = !$db->inTransaction(); if ($owns) { $db->beginTransaction(); } try { $db->prepare( "INSERT INTO asm3_import_runs(source_name,source_sha256,status,summary_json,backup_name) VALUES(:n,:h,'completed','{}',:b)", )->execute([':n' => basename($path) . ' · portées', ':h' => $hash, ':b' => $result['backup']]); $run = (int) $db->lastInsertId(); foreach ($src['animallitter'] ?? [] as $l) { $source = (int) $l['ID']; $seen = $db->prepare( "SELECT 1 FROM asm3_import_links WHERE source_sha256=:h AND entity_type='litter' AND source_id=:s", ); $seen->execute([':h' => $hash, ':s' => $source]); if ($seen->fetchColumn()) { continue; } $parent = (int) $l['PARENTANIMALID']; $kids = array_values( array_filter($animals[(string) $l['ACCEPTANCENUMBER']] ?? [], fn($id) => $id !== $parent), ); if (!isset($links[$parent]) || array_filter($kids, fn($id) => !isset($links[$id]))) { continue; } $db->prepare('INSERT INTO litters(mother_id,birth_date,notes) VALUES(:parent,:date,:notes)')->execute([ ':parent' => $links[$parent], ':date' => substr((string) $l['DATE'], 0, 10), ':notes' => trim((string) $l['COMMENTS']) ?: null, ]); $target = (int) $db->lastInsertId(); $ins = $db->prepare( 'INSERT OR IGNORE INTO litter_kittens(litter_id,animal_id,position) VALUES(:litter,:animal,:position)', ); foreach ($kids as $i => $kid) { $ins->execute([':litter' => $target, ':animal' => $links[$kid], ':position' => $i + 1]); $result['kittens_linked']++; } $db->prepare( "INSERT INTO asm3_import_links(source_sha256,entity_type,source_id,target_id,import_run_id) VALUES(:h,'litter',:s,:t,:r)", )->execute([':h' => $hash, ':s' => $source, ':t' => $target, ':r' => $run]); $result['created']++; } if ($owns) { $db->commit(); } } catch (Throwable $e) { if ($owns && $db->inTransaction()) { $db->rollBack(); } throw $e; } return $result; } }