option('table')) ?: 'settings_copy1'; $dryRun = (bool) $this->option('dry-run'); $overwrite = (bool) $this->option('overwrite'); $fillFromSuperAdmin = (bool) $this->option('fill-from-superadmin'); if (! Schema::hasTable($table)) { $this->error("جدول بکاپ «{$table}» وجود ندارد."); return self::FAILURE; } $backupRows = $this->readBackup($table); $superAdminValues = $fillFromSuperAdmin ? $this->superAdminValues() : []; if ($backupRows === [] && $superAdminValues === []) { $this->warn('هیچ کلید قابل بازگردانی پیدا نشد.'); return self::SUCCESS; } $keys = array_values(array_unique(array_merge( array_keys($backupRows), array_keys($superAdminValues) ))); sort($keys); $localRows = Setting::query() ->whereIn('key', $keys) ->get() ->keyBy('key'); $hasType = Setting::hasTypeColumn(); $report = []; $counts = ['inserted' => 0, 'updated' => 0, 'skipped' => 0]; foreach ($keys as $key) { $backup = $backupRows[$key] ?? null; $local = $localRows->get($key); $value = $backup['value'] ?? null; $source = $value === null || $value === '' ? '' : 'backup'; if (($value === null || $value === '') && array_key_exists($key, $superAdminValues)) { $value = $superAdminValues[$key]; $source = 'superadmin'; } if (! $local) { $counts['inserted']++; $report[] = [$key, '—', $this->mask($key, $value), 'INSERT '.($source ?: 'empty')]; if (! $dryRun) { $row = new Setting(); $row->key = $key; $row->value = $value ?? ''; $row->title_fa = $backup['title_fa'] ?? $key; $row->description = $backup['description'] ?? ''; $row->is_active = $backup['is_active'] ?? 1; $row->can_edit = 0; if ($hasType) { $row->type = $backup['type'] ?? ''; } $row->save(); } continue; } $localValue = (string) ($local->value ?? ''); $writeValue = $value !== null && $value !== '' && ($localValue === '' || ($overwrite && $localValue !== (string) $value)); $metaChanged = false; if ($backup) { $metaChanged = $this->metaDiffers($local, $backup, $hasType); } if (! $writeValue && ! $metaChanged) { $counts['skipped']++; continue; } $counts['updated']++; $report[] = [ $key, $this->mask($key, $localValue), $writeValue ? $this->mask($key, $value) : '(بدون تغییر)', $writeValue ? 'UPDATE '.$source : 'META', ]; if ($dryRun) { continue; } if ($writeValue) { $local->value = $value; } if ($backup) { $local->title_fa = $backup['title_fa'] ?: ($local->title_fa ?: $key); if (($backup['description'] ?? '') !== '') { $local->description = $backup['description']; } if ($hasType && ($backup['type'] ?? '') !== '') { $local->type = $backup['type']; } } $local->save(); } if ($report !== []) { $this->table(['key', 'مقدار لوکال', 'مقدار جدید', 'اقدام'], $report); } $this->info(sprintf( '%sinsert=%d update=%d skip=%d (backup=%s, superadmin=%d)', $dryRun ? '[dry-run] ' : '', $counts['inserted'], $counts['updated'], $counts['skipped'], $table, count($superAdminValues) )); if ($dryRun) { $this->comment('هیچ چیزی نوشته نشد. برای اجرا همین دستور را بدون --dry-run بزن.'); } return self::SUCCESS; } /** * ردیف‌های can_edit=0 بکاپ، بدون کلیدهای سوپرادمین و runtime. * * @return array> */ private function readBackup(string $table): array { $hasType = Schema::hasColumn($table, 'type'); $columns = ['key', 'value', 'title_fa', 'description', 'is_active', 'can_edit']; if ($hasType) { $columns[] = 'type'; } $rows = []; foreach (DB::table($table)->select($columns)->orderBy('key')->get() as $row) { $key = trim((string) ($row->key ?? '')); if ($key === '' || ! $this->isRestorable($key)) { continue; } if ((int) ($row->can_edit ?? 0) !== 0) { continue; } $rows[$key] = [ 'value' => $row->value === null ? '' : (string) $row->value, 'title_fa' => (string) ($row->title_fa ?? ''), 'description' => (string) ($row->description ?? ''), 'is_active' => (int) ($row->is_active ?? 1), 'type' => $hasType ? (string) ($row->type ?? '') : '', ]; } return $rows; } /** * مقدار غیرخالی کلیدهای غیرسوپرادمینی که هنوز در سوپرادمین مانده‌اند. * * @return array */ private function superAdminValues(): array { $items = app(SuperAdminProgramSettingsClient::class)->fetchItems(); if (! is_array($items)) { $this->warn('سوپرادمین در دسترس نبود — فالبک نادیده گرفته شد.'); return []; } $values = []; foreach ($items as $item) { if (! is_array($item)) { continue; } $key = trim((string) ($item['key'] ?? '')); if ($key === '' || ! $this->isRestorable($key)) { continue; } $value = $item['value'] ?? null; if ($value === null || $value === '') { continue; } $values[$key] = (string) $value; } return $values; } private function isRestorable(string $key): bool { if (SuperAdminProgramSettingsOverlay::isSuperAdminOwned($key)) { return false; } return ! SuperAdminProgramSettingsOverlay::isRuntime($key); } /** @param array $backup */ private function metaDiffers(Setting $local, array $backup, bool $hasType): bool { if (($backup['title_fa'] ?? '') !== '' && (string) ($local->title_fa ?? '') !== $backup['title_fa']) { return true; } if (($backup['description'] ?? '') !== '' && (string) ($local->description ?? '') !== $backup['description']) { return true; } return $hasType && ($backup['type'] ?? '') !== '' && (string) ($local->type ?? '') !== $backup['type']; } private function mask(string $key, ?string $value): string { $value = (string) $value; if ($value === '') { return '(خالی)'; } if (Setting::isSecretOrServerOnlyKey($key)) { return '***('.mb_strlen($value).' نویسه)'; } return mb_strlen($value) > 40 ? mb_substr($value, 0, 37).'…' : $value; } }