$_GET['table'] ?? null, 'all' => isset($_GET['all']) ? '1' : null, 'limit' => $_GET['limit'] ?? null, 'dry-run' => isset($_GET['dry_run']) ? '1' : null, 'lookup' => $_GET['lookup'] ?? null, ]; } if (!empty($opt['lookup'])) $LOOKUP_URL = (string)$opt['lookup']; $tableFilter = $opt['table'] ?? null; $doAll = isset($opt['all']) || !$tableFilter; $limit = isset($opt['limit']) ? max(1,(int)$opt['limit']) : 500; $dryRun = isset($opt['dry-run']); // ---------- db connect ---------- $dsn = "mysql:host={$DB_HOST};port={$DB_PORT};dbname={$DB_NAME};charset=utf8mb4"; $pdo = new PDO($dsn, $DB_USER, $DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); // ---------- helpers ---------- function info($s){ fwrite(STDOUT, $s . PHP_EOL); } function warnln($s){ fwrite(STDERR, "[WARN] $s" . PHP_EOL); } function table_has_cols(PDO $pdo, string $t, array $cols): array { $have = []; $st = $pdo->query("SHOW COLUMNS FROM `{$t}`"); while ($r = $st->fetch()) $have[strtolower($r['Field'])] = true; $out = []; foreach ($cols as $c) $out[$c] = isset($have[strtolower($c)]); return $out; } function ensure_cols(PDO $pdo, string $t){ // Use your live schema names: property_id + title_reference (+ optional areas) $need = table_has_cols($pdo, $t, ['property_id','title_reference','area_sqm','area_ha']); $sqls = []; if (!$need['property_id']) $sqls[] = "ALTER TABLE `{$t}` ADD COLUMN property_id VARCHAR(32) NULL"; if (!$need['title_reference']) $sqls[] = "ALTER TABLE `{$t}` ADD COLUMN title_reference VARCHAR(64) NULL"; if (!$need['area_sqm']) $sqls[] = "ALTER TABLE `{$t}` ADD COLUMN area_sqm DOUBLE NULL"; if (!$need['area_ha']) $sqls[] = "ALTER TABLE `{$t}` ADD COLUMN area_ha DOUBLE NULL"; foreach ($sqls as $sql) { $pdo->exec($sql); } } function post_json(string $url, array $payload, int $timeout=15): array { $ch = curl_init($url); $body = json_encode($payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, CURLOPT_CONNECTTIMEOUT => $timeout, CURLOPT_TIMEOUT => $timeout, CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, ]); $resp = curl_exec($ch); if ($resp === false) { $err = curl_error($ch); curl_close($ch); throw new RuntimeException("cURL error: $err"); } $code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE); curl_close($ch); if ($code < 200 || $code >= 300) throw new RuntimeException("HTTP $code from lookup"); $j = json_decode($resp, true); if (!is_array($j)) throw new RuntimeException("Bad JSON from lookup"); return $j; } // ---------- pick tables ---------- $tables = []; if ($doAll) { $st = $pdo->query("SHOW TABLES"); while ($r = $st->fetch(PDO::FETCH_NUM)) { if (strpos($r[0], 'da_') === 0) $tables[] = $r[0]; } } else { $tables = [$tableFilter]; } if (!$tables) { info("No tables."); exit(0); } // ---------- process ---------- foreach ($tables as $t) { info("Table: {$t}"); // Needs lat/lng $has = table_has_cols($pdo, $t, ['lat','lng']); if (!$has['lat'] || !$has['lng']) { info(" skip (no lat/lng)"); continue; } ensure_cols($pdo, $t); // Rows that need fill (lat/lng present; missing property_id or title_reference) $sql = " SELECT id, lat, lng, property_id, title_reference FROM `{$t}` WHERE lat IS NOT NULL AND lng IS NOT NULL AND (title_reference IS NULL OR title_reference = '') ORDER BY id ASC LIMIT {$limit}"; $todo = $pdo->query($sql)->fetchAll(); if (!$todo) { info(" nothing to do"); continue; } info(" found ".count($todo)." row(s) to backfill (limit {$limit})"); $upd = $pdo->prepare(" UPDATE `{$t}` SET property_id = ?, title_reference = ?, area_sqm = ?, area_ha = ? WHERE id = ?"); foreach ($todo as $r) { $id = (int)$r['id']; $lat = (float)$r['lat']; $lng = (float)$r['lng']; try { $resp = post_json($LOOKUP_URL, ['lat'=>$lat,'lng'=>$lng]); if (empty($resp['ok'])) throw new RuntimeException($resp['error'] ?? 'lookup failed'); $pid = isset($resp['pid']) ? preg_replace('/[^0-9]/','', (string)$resp['pid']) : null; $titleId = $resp['title_id'] ?? null; $areaSqm = isset($resp['area_sqm']) ? (float)$resp['area_sqm'] : null; $areaHa = isset($resp['area_ha']) ? (float)$resp['area_ha'] : null; if ($dryRun) { info(sprintf(" id=%d lat=%.6f lng=%.6f -> pid=%s title=%s area=%.1f sqm", $id, $lat, $lng, ($pid?:'NULL'), ($titleId?:'NULL'), ($areaSqm?:0))); } else { $upd->execute([$pid, $titleId, $areaSqm, $areaHa, $id]); } } catch (Throwable $e) { warnln(" id={$id} error: ".$e->getMessage()); } if ($THROTTLE_US > 0) usleep($THROTTLE_US); } info(" done {$t}"); } info("All done.");