netgescon-day0/app/Support/Livewire/SupportsRubricaLookup.php

180 lines
6.7 KiB
PHP

<?php
namespace App\Support\Livewire;
use App\Models\RubricaUniversale;
use BackedEnum;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
trait SupportsRubricaLookup
{
/**
* Normalizza input Livewire eterogenei in una stringa di ricerca stabile.
*/
protected function normalizeRubricaLookupText($value): string
{
if (is_array($value)) {
$value = Arr::first(Arr::flatten($value));
}
if ($value instanceof BackedEnum) {
$value = $value->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<string,mixed>
*/
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<int,string>|null $categories
* @return array<int,array<string,mixed>>
*/
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<string,mixed> $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;
}
}