argument('table')); $pk = trim((string) $this->argument('pk')); $pkColumn = trim((string) $this->option('pk-column')) ?: 'id'; if ($table === '' || $pk === '') { $this->error('Parametri table/pk obbligatori.'); return self::FAILURE; } $snapshotDir = $this->resolveSnapshotDir(); if ($snapshotDir === null) { $this->error('Snapshot non trovato.'); return self::FAILURE; } $file = $snapshotDir . '/db/' . $table . '.ndjson'; if (! is_file($file)) { $this->error('Export tabella non trovato nello snapshot: ' . $file); return self::FAILURE; } $record = $this->findRecordByPk($file, $pk); if (! is_array($record)) { $this->error('Record non trovato nello snapshot per PK=' . $pk); return self::FAILURE; } $this->line('Snapshot: ' . $snapshotDir); $this->line('Tabella: ' . $table . ' ยท PK: ' . $pkColumn . '=' . $pk); $this->line('Hash: ' . (string) ($record['hash'] ?? '-')); $data = is_array($record['data'] ?? null) ? (array) $record['data'] : []; if (! array_key_exists($pkColumn, $data)) { $data[$pkColumn] = $pk; } if (! (bool) $this->option('apply')) { $this->warn('Preview mode: nessuna scrittura eseguita. Usa --apply per ripristinare.'); $this->line(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)); return self::SUCCESS; } if (! DB::getSchemaBuilder()->hasTable($table)) { $this->error('Tabella non esistente su DB corrente: ' . $table); return self::FAILURE; } DB::table($table)->updateOrInsert( [$pkColumn => $data[$pkColumn]], $data ); $this->info('Record ripristinato con successo.'); return self::SUCCESS; } private function resolveSnapshotDir(): ?string { $snapshotId = trim((string) $this->option('snapshot-id')); $root = storage_path('app/update-backups/snapshots'); if ($snapshotId !== '') { $dir = $root . '/' . $snapshotId; return is_dir($dir) ? $dir : null; } $latest = storage_path('app/update-backups/latest-manifest.json'); if (is_file($latest)) { $decoded = json_decode((string) @file_get_contents($latest), true); if (is_array($decoded) && isset($decoded['snapshot_dir']) && is_string($decoded['snapshot_dir']) && is_dir($decoded['snapshot_dir'])) { return $decoded['snapshot_dir']; } } if (! is_dir($root)) { return null; } $dirs = array_values(array_filter(scandir($root) ?: [], function (string $name) use ($root): bool { return $name !== '.' && $name !== '..' && is_dir($root . '/' . $name); })); rsort($dirs, SORT_NATURAL); if ($dirs === []) { return null; } return $root . '/' . $dirs[0]; } /** @return array|null */ private function findRecordByPk(string $file, string $pk): ?array { $handle = fopen($file, 'rb'); if (! is_resource($handle)) { return null; } try { while (($line = fgets($handle)) !== false) { $decoded = json_decode((string) $line, true); if (! is_array($decoded)) { continue; } $candidate = (string) ($decoded['pk'] ?? ''); if ($candidate === $pk) { return $decoded; } } } finally { fclose($handle); } return null; } }