'Unauthorized']); exit; } require_once __DIR__ . '/class/VisualLimsApiClient.class.php'; header('Content-Type: application/json'); ini_set('display_errors', '0'); error_reporting(E_ALL); $fieldId = intval($_GET['field_id'] ?? 0); $q = mb_strtolower(trim($_GET['q'] ?? '')); $id = isset($_GET['id']) ? intval($_GET['id']) : null; $rawLimit = intval($_GET['limit'] ?? 20); $limit = $rawLimit <= 0 ? 0 : max(1, min(500, $rawLimit)); if (!$fieldId) { echo json_encode(['results' => []]); exit; } try { $cacheDir = __DIR__ . '/cache'; $cacheFile = $cacheDir . '/customfield_' . $fieldId . '.json'; if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) { $values = json_decode(file_get_contents($cacheFile), true); } else { $api = VisualLimsApiClient::getInstance(); $data = $api->get("CustomField($fieldId)?\$expand=CustomFieldsValues"); $values = $data['CustomFieldsValues'] ?? []; if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true); file_put_contents($cacheFile, json_encode($values)); } // Lookup by ID if ($id !== null) { foreach ($values as $v) { if ((int)($v['IdCustomFieldsValue'] ?? 0) === $id) { echo json_encode(['results' => [['id' => $v['IdCustomFieldsValue'], 'text' => $v['Valore'] ?? '']]]); exit; } } echo json_encode(['results' => []]); exit; } // Search — multi-term AND, position-independent, ranked $terms = array_values(array_filter( preg_split('/\s+/u', $q, -1, PREG_SPLIT_NO_EMPTY) )); // Nessun termine digitato: alfabetico, taglio al limite if (empty($terms)) { $all = []; foreach ($values as $v) { $all[] = ['id' => $v['IdCustomFieldsValue'], 'text' => $v['Valore'] ?? '']; } usort($all, fn($a, $b) => strcasecmp($a['text'], $b['text'])); if ($limit > 0) $all = array_slice($all, 0, $limit); echo json_encode(['results' => $all]); exit; } $firstTerm = $terms[0]; $scored = []; foreach ($values as $v) { $text = $v['Valore'] ?? ''; $textLower = mb_strtolower($text); // AND: tutti i termini devono essere presenti, in qualsiasi posizione $matchAll = true; foreach ($terms as $t) { if (mb_strpos($textLower, $t) === false) { $matchAll = false; break; } } if (!$matchAll) continue; // Punteggio di pertinenza (più basso = più rilevante) if ($textLower === $q) { $score = 0; // match esatto } elseif (mb_strpos($textLower, $firstTerm) === 0) { $score = 1; // inizia col primo termine } elseif (preg_match('/(^|\s)' . preg_quote($firstTerm, '/') . '/u', $textLower)) { $score = 2; // primo termine a inizio di una parola } else { $score = 3; // presente ma non a inizio parola } $scored[] = [ 'id' => $v['IdCustomFieldsValue'], 'text' => $text, '_s' => $score, '_n' => $textLower ]; } // Ordina per punteggio, poi alfabetico usort($scored, function ($a, $b) { if ($a['_s'] !== $b['_s']) return $a['_s'] - $b['_s']; return strcmp($a['_n'], $b['_n']); }); // Taglia DOPO l'ordinamento e rimuovi i campi interni if ($limit > 0) { $scored = array_slice($scored, 0, $limit); } $results = array_map( fn($r) => ['id' => $r['id'], 'text' => $r['text']], $scored ); echo json_encode(['results' => $results]); } catch (Exception $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage()]); }