$apply ? 'apply' : 'dry-run', 'entity' => 'contacts', 'planned' => $plan['summary'], 'created' => 0, 'linked_matches' => 0, 'skipped_match' => 0, 'skipped_review' => $plan['summary']['review'], 'backup' => null, 'read_only' => !$apply, 'would_create' => $plan['summary']['create'], ]; if (!$apply) { return $result; } if ($createBackup) { $backup = BackupService::create('pre-asm3-import'); $result['backup'] = $backup['name'] ?? null; } $ownsTransaction = !$db->inTransaction(); if ($ownsTransaction) { $db->beginTransaction(); } try { $db->prepare( "INSERT INTO asm3_import_runs(source_name,source_sha256,status,summary_json,backup_name) VALUES(:name,:hash,'completed','{}',:backup)", )->execute([':name' => basename($path) . ' ยท contacts', ':hash' => $hash, ':backup' => $result['backup']]); $runId = (int) $db->lastInsertId(); foreach ($plan['items'] as $item) { if ($item['action'] === 'review') { continue; } $check = $db->prepare( "SELECT target_id FROM asm3_import_links WHERE source_sha256=:hash AND entity_type='contact' AND source_id=:source", ); $check->execute([':hash' => $hash, ':source' => $item['asm3_id']]); if ($check->fetchColumn()) { continue; } $c = $item['mapped']; if ($item['action'] === 'match') { $target = (int) ($item['globinours_candidates'][0]['id'] ?? 0); if ($target <= 0) { $result['skipped_match']++; continue; } $result['linked_matches']++; } else { $db->prepare( 'INSERT INTO directory_contacts(kind,name,phone,email,address,postal_code,city,country,notes) VALUES(:kind,:name,:phone,:email,:address,:postal,:city,:country,:notes)', )->execute([ ':kind' => $c['kind'], ':name' => $c['name'], ':phone' => $c['phone'], ':email' => $c['email'], ':address' => $c['address'], ':postal' => $c['postal_code'], ':city' => $c['city'], ':country' => $c['country'], ':notes' => $c['notes'], ]); $target = (int) $db->lastInsertId(); $result['created']++; } $role = $db->prepare( 'INSERT OR IGNORE INTO directory_contact_roles(contact_id,role) VALUES(:contact,:role)', ); foreach ($c['roles'] as $value) { $role->execute([':contact' => $target, ':role' => $value]); } $db->prepare( "INSERT INTO asm3_import_links(source_sha256,entity_type,source_id,target_id,import_run_id) VALUES(:hash,'contact',:source,:target,:run)", )->execute([':hash' => $hash, ':source' => $item['asm3_id'], ':target' => $target, ':run' => $runId]); } $db->prepare('UPDATE asm3_import_runs SET summary_json=:summary WHERE id=:id')->execute([ ':summary' => json_encode($result, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), ':id' => $runId, ]); if ($ownsTransaction) { $db->commit(); } } catch (Throwable $e) { if ($ownsTransaction && $db->inTransaction()) { $db->rollBack(); } throw $e; } return $result; } }