error('Tabella rubrica_universale non disponibile.'); return self::FAILURE; } $apply = (bool) $this->option('apply'); $limit = max(1, min((int) $this->option('limit'), 50000)); $ids = array_values(array_filter(array_map(fn($value) => is_numeric($value) ? (int) $value : 0, (array) $this->option('id')))); $query = RubricaUniversale::query() ->where('note', 'like', '%Dati anagrafici legacy:%') ->orderBy('id') ->limit($limit); if ($ids !== []) { $query->whereIn('id', $ids); } $rows = $query->get(); if ($rows->isEmpty()) { $this->info('Nessun record da bonificare.'); return self::SUCCESS; } $processed = 0; $updated = 0; foreach ($rows as $rubrica) { $processed++; $legacy = $this->extractLegacyIdentityFromNote($rubrica->note); $changes = []; if (! $rubrica->titolo_id && ! empty($legacy['titolo_id'])) { $changes['titolo_id'] = (int) $legacy['titolo_id']; } if (! $rubrica->sesso && ! empty($legacy['sesso'])) { $changes['sesso'] = $legacy['sesso']; } if ((! $rubrica->data_nascita || (int) optional($rubrica->data_nascita)->format('Y') > (int) now()->format('Y') + 1) && ! empty($legacy['data_nascita'])) { $changes['data_nascita'] = $legacy['data_nascita']; } if (! $rubrica->luogo_nascita && ! empty($legacy['luogo_nascita'])) { $changes['luogo_nascita'] = $legacy['luogo_nascita']; } if (! $rubrica->provincia_nascita && ! empty($legacy['provincia_nascita'])) { $changes['provincia_nascita'] = $legacy['provincia_nascita']; } $cleanNote = $legacy['note_clean'] ?? null; if (($rubrica->note ?? null) !== $cleanNote) { $changes['note'] = $cleanNote; } if ($changes === []) { continue; } $updated++; if ($apply) { $rubrica->fill($changes); $rubrica->save(); } } $this->info(($apply ? 'Bonifica eseguita' : 'Dry-run bonifica') . ": processati {$processed}, aggiornabili {$updated}"); return self::SUCCESS; } /** @return array */ private function extractLegacyIdentityFromNote(mixed $note): array { $raw = trim((string) ($note ?? '')); if ($raw === '') { return [ 'note_clean' => null, 'titolo_id' => null, 'sesso' => null, 'data_nascita' => null, 'luogo_nascita' => null, 'provincia_nascita' => null, ]; } $parsed = [ 'titolo_id' => null, 'sesso' => null, 'data_nascita' => null, 'luogo_nascita' => null, 'provincia_nascita' => null, ]; $cleanLines = []; foreach (preg_split('/\r\n|\r|\n/', $raw) ?: [] as $line) { $trimmed = trim($line); if (! str_starts_with($trimmed, 'Dati anagrafici legacy:')) { if ($trimmed !== '') { $cleanLines[] = $line; } continue; } $payload = trim(substr($trimmed, strlen('Dati anagrafici legacy:'))); $bits = preg_split('/\s*\|\s*/', $payload) ?: []; foreach ($bits as $bit) { [$key, $value] = array_pad(explode(':', $bit, 2), 2, null); $key = mb_strtolower(trim((string) $key)); $value = trim((string) $value); if ($value === '') { continue; } if ($key === 'titolo') { $parsed['titolo_id'] = $this->resolveTitoloId($value); continue; } if ($key === 'sesso') { $parsed['sesso'] = $this->normalizeSessoValue($value); continue; } if ($key === 'data nascita') { $parsed['data_nascita'] = $this->normalizeLegacyDateValue($value); continue; } if ($key === 'luogo nascita') { $parsed['luogo_nascita'] = $value; continue; } if ($key === 'provincia nascita') { $parsed['provincia_nascita'] = $this->normalizeProvince($value); } } } return $parsed + [ 'note_clean' => $cleanLines === [] ? null : implode("\n", $cleanLines), ]; } private function normalizeLegacyDateValue(mixed $value): ?string { $raw = trim((string) ($value ?? '')); if ($raw === '') { return null; } foreach (['Y-m-d', 'd/m/Y', 'd-m-Y', 'm/d/Y', 'd.m.Y', 'd/m/y', 'd-m-y', 'm/d/y', 'd.m.y'] as $format) { $dt = \DateTime::createFromFormat($format, $raw); if ($dt instanceof \DateTime) { return $this->normalizeBirthDateYear($dt); } } $timestamp = strtotime($raw); if ($timestamp === false) { return null; } return $this->normalizeBirthDateYear((new \DateTime())->setTimestamp($timestamp)); } private function normalizeBirthDateYear(\DateTime $date): ?string { $year = (int) $date->format('Y'); $currentYear = (int) now()->format('Y'); if ($year > $currentYear + 1) { $date->modify('-100 years'); $year = (int) $date->format('Y'); } if ($year < 1900 || $year > $currentYear + 1) { return null; } return $date->format('Y-m-d'); } private function normalizeSessoValue(mixed $value): ?string { $raw = strtoupper(trim((string) ($value ?? ''))); return match (true) { $raw === 'M', str_starts_with($raw, 'MAS') => 'M', $raw === 'F', str_starts_with($raw, 'FEM') => 'F', default => null, }; } private function normalizeProvince(mixed $value): ?string { $raw = strtoupper(trim((string) ($value ?? ''))); if ($raw === '') { return null; } return mb_substr($raw, 0, 2); } private function resolveTitoloId(string $value): ?int { if (! Schema::hasTable('titoli')) { return null; } $key = mb_strtolower(trim($value)); if ($key === '') { return null; } $titolo = Titolo::query() ->whereRaw('LOWER(sigla) = ?', [$key]) ->orWhereRaw('LOWER(nome) = ?', [$key]) ->first(); return $titolo?->id ? (int) $titolo->id : null; } }