value; } if (! is_scalar($value) && $value !== null) { return ''; } return trim((string) ($value ?? '')); } protected function normalizeRubricaLookupId($value): ?int { $normalized = $this->normalizeRubricaLookupText($value); if ($normalized === '' || ! is_numeric($normalized)) { return null; } $id = (int) $normalized; return $id > 0 ? $id : null; } protected function formatRubricaLookupLabel(?RubricaUniversale $rubrica): string { if (! $rubrica instanceof RubricaUniversale) { return ''; } $label = trim((string) ($rubrica->ragione_sociale ?: $rubrica->nome_completo)); return $label !== '' ? $label : ('Rubrica #' . (int) $rubrica->id); } /** * Mappa una riga rubrica in un payload leggero e riusabile nelle dropdown live. * * @return array */ protected function mapRubricaLookupRow(RubricaUniversale $rubrica): array { $phone = trim((string) ($rubrica->telefono_ufficio ?: $rubrica->telefono_cellulare ?: $rubrica->telefono_casa)); return [ 'id' => (int) $rubrica->id, 'label' => $this->formatRubricaLookupLabel($rubrica), 'email' => trim((string) ($rubrica->email ?? '')), 'phone' => $phone, 'category' => (string) ($rubrica->categoria ?? ''), 'cf' => trim((string) ($rubrica->codice_fiscale ?? '')), 'piva' => trim((string) ($rubrica->partita_iva ?? '')), 'note' => Str::limit(trim((string) ($rubrica->note ?? '')), 100), ]; } /** * Ricerca libera su rubrica con deduplica label e fallback sulla selezione corrente. * * @param array|null $categories * @return array> */ protected function searchRubricaLookupMatches(string $raw, ?int $selectedId = null, ?array $categories = null, int $limit = 12, ?int $amministratoreId = null): array { $raw = $this->normalizeRubricaLookupText($raw); if ($raw === '') { if (($selectedId ?? 0) > 0) { $selected = RubricaUniversale::query()->find($selectedId); return $selected instanceof RubricaUniversale ? [$this->mapRubricaLookupRow($selected)] : []; } return []; } $needle = '%' . mb_strtolower($raw) . '%'; $digits = preg_replace('/\D+/', '', $raw) ?: ''; $rows = []; $query = RubricaUniversale::query() ->whereNull('deleted_at') ->where(function ($query) use ($needle, $digits): void { $query->whereRaw("LOWER(COALESCE(ragione_sociale, '')) LIKE ?", [$needle]) ->orWhereRaw("LOWER(COALESCE(nome, '')) LIKE ?", [$needle]) ->orWhereRaw("LOWER(COALESCE(cognome, '')) LIKE ?", [$needle]) ->orWhereRaw("LOWER(COALESCE(email, '')) LIKE ?", [$needle]) ->orWhereRaw("LOWER(COALESCE(note, '')) LIKE ?", [$needle]) ->orWhereRaw("LOWER(COALESCE(codice_fiscale, '')) LIKE ?", [$needle]) ->orWhereRaw("LOWER(COALESCE(partita_iva, '')) LIKE ?", [$needle]); if ($digits !== '') { $query->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_ufficio, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%']) ->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_cellulare, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%']) ->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_casa, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%']); } }) ->orderByRaw('CASE WHEN id = ? THEN 0 ELSE 1 END', [(int) ($selectedId ?? 0)]) ->orderBy('ragione_sociale') ->orderBy('cognome') ->orderBy('nome') ->limit(80); if ($categories !== null && $categories !== []) { $query->whereIn('categoria', $categories); } if ($amministratoreId !== null && $amministratoreId > 0 && method_exists(new RubricaUniversale(), 'getTable')) { $query->where(function ($builder) use ($amministratoreId): void { $builder->where('amministratore_id', $amministratoreId) ->orWhereNull('amministratore_id'); }); } $matches = $query->get(); foreach ($matches as $rubrica) { $row = $this->mapRubricaLookupRow($rubrica); $row['score'] = $this->scoreRubricaLookupMatch($row, $raw, $digits, $selectedId); $rows[] = $row; } usort($rows, fn(array $left, array $right): int => ((int) $right['score']) <=> ((int) $left['score'])); $rows = array_values(array_slice(array_map(function (array $row): array { unset($row['score']); return $row; }, $rows), 0, $limit)); return $rows; } /** * @param array $row */ protected function scoreRubricaLookupMatch(array $row, string $raw, string $digits, ?int $selectedId = null): int { $score = (int) (($selectedId ?? 0) > 0 && (int) ($row['id'] ?? 0) === (int) $selectedId ? 1000 : 0); $haystack = mb_strtolower(implode(' ', array_filter([ (string) ($row['label'] ?? ''), (string) ($row['email'] ?? ''), (string) ($row['phone'] ?? ''), (string) ($row['cf'] ?? ''), (string) ($row['piva'] ?? ''), (string) ($row['category'] ?? ''), (string) ($row['note'] ?? ''), ]))); if ($raw !== '' && str_contains($haystack, mb_strtolower($raw))) { $score += 90; } if ($digits !== '') { $phoneDigits = preg_replace('/\D+/', '', (string) ($row['phone'] ?? '')) ?: ''; if ($phoneDigits !== '' && str_contains($phoneDigits, $digits)) { $score += 70; } } return $score; } }