feat: try fix meilisearch

This commit is contained in:
Leon 2026-01-30 12:08:17 +01:00
parent c241e67c90
commit 30f253b2d6
1 changed files with 27 additions and 36 deletions

View File

@ -15,22 +15,16 @@ class HuntsController extends Controller
{ {
try { try {
$searchTerm = $this->resolveSearchTerm($request); $searchTerm = $this->resolveSearchTerm($request);
// Construire les filtres Meilisearch
$filters = $this->buildMeilisearchFilters($request); $filters = $this->buildMeilisearchFilters($request);
// Construire la recherche
$search = Hunts::search($searchTerm ?? '') $search = Hunts::search($searchTerm ?? '')
->query(fn ($query) => $query->with('participants:id,avatar_uri')); ->query(fn ($query) => $query->with('participants:id,avatar_uri'));
// N'ajouter le filter que s'il n'est pas vide // ✅ IMPORTANT: nenvoie PAS filter sil est vide
if ($filters !== '') { if ($filters !== '') {
$search->options([ $search->options(['filter' => $filters]);
'filter' => $filters,
]);
} }
// (utile en prod pour vérifier ce qui est réellement envoyé)
Log::info('HUNTS_SEARCH', [ Log::info('HUNTS_SEARCH', [
'q' => $searchTerm ?? '', 'q' => $searchTerm ?? '',
'filters' => $filters, 'filters' => $filters,
@ -38,9 +32,7 @@ class HuntsController extends Controller
'index' => (new Hunts)->searchableAs(), 'index' => (new Hunts)->searchableAs(),
]); ]);
$hunts = $search->paginate(10); return response()->json($search->paginate(10));
return response()->json($hunts);
} catch (\Throwable $e) { } catch (\Throwable $e) {
Log::error('Error fetching hunts: '.$e->getMessage()); Log::error('Error fetching hunts: '.$e->getMessage());
@ -63,13 +55,16 @@ class HuntsController extends Controller
FILTER_NULL_ON_FAILURE FILTER_NULL_ON_FAILURE
); );
$userId = auth('sanctum')->id(); $userId = auth('sanctum')->id(); // chez toi => UUID string
if ($userId) { if ($userId) {
$escapedUserId = $this->escapeMeiliString((string) $userId);
if ($participating === true) { if ($participating === true) {
$filters[] = "participant_ids = {$userId}"; // ✅ UUID => string
$filters[] = "participant_ids = \"{$escapedUserId}\"";
} elseif ($participating === false) { } elseif ($participating === false) {
$filters[] = "participant_ids != {$userId}"; $filters[] = "participant_ids != \"{$escapedUserId}\"";
} }
} elseif ($participating === true) { } elseif ($participating === true) {
$filters[] = "id < 0"; $filters[] = "id < 0";
@ -78,7 +73,7 @@ class HuntsController extends Controller
// active // active
if ($request->has('active')) { if ($request->has('active')) {
$now = Carbon::now('UTC')->timestamp; // ✅ UTC $now = Carbon::now('UTC')->timestamp;
$active = filter_var($request->query('active'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); $active = filter_var($request->query('active'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
if ($active === true) { if ($active === true) {
@ -93,8 +88,7 @@ class HuntsController extends Controller
if ($request->filled('city')) { if ($request->filled('city')) {
$city = $request->query('city'); $city = $request->query('city');
if (is_string($city)) { if (is_string($city)) {
// ✅ échapper correctement les quotes $city = $this->escapeMeiliString($city);
$city = str_replace('"', '\"', $city);
$filters[] = "city = \"{$city}\""; $filters[] = "city = \"{$city}\"";
} }
} }
@ -114,17 +108,20 @@ class HuntsController extends Controller
)); ));
if (!empty($validDifficulties)) { if (!empty($validDifficulties)) {
$difficultyFilters = array_map(fn ($d) => "difficulty = \"{$d}\"", $validDifficulties); $difficultyFilters = array_map(
fn ($d) => 'difficulty = "'.$this->escapeMeiliString($d).'"',
$validDifficulties
);
$filters[] = '(' . implode(' OR ', $difficultyFilters) . ')'; $filters[] = '(' . implode(' OR ', $difficultyFilters) . ')';
} }
} }
} }
// ✅ Dates (UTC + end-of-day si nécessaire) // dates (UTC)
$startFrom = $this->parseDateFilterUtc($request->query('start_from')); $startFrom = $this->parseDateFilterUtc($request->query('start_from'));
$startTo = $this->parseDateFilterUtc($request->query('start_to'), true); $startTo = $this->parseDateFilterUtc($request->query('start_to'));
$endFrom = $this->parseDateFilterUtc($request->query('end_from')); $endFrom = $this->parseDateFilterUtc($request->query('end_from'));
$endTo = $this->parseDateFilterUtc($request->query('end_to'), true); $endTo = $this->parseDateFilterUtc($request->query('end_to'));
if ($startFrom) { if ($startFrom) {
$filters[] = "start_at_timestamp >= {$startFrom->timestamp}"; $filters[] = "start_at_timestamp >= {$startFrom->timestamp}";
@ -155,29 +152,23 @@ class HuntsController extends Controller
return $searchTerm !== '' ? $searchTerm : null; return $searchTerm !== '' ? $searchTerm : null;
} }
/** private function parseDateFilterUtc(mixed $value): ?Carbon
* Parse une date en UTC.
* Si $endOfDay=true et que l'entrée ressemble à une date "YYYY-MM-DD",
* on la transforme en fin de journée UTC.
*/
private function parseDateFilterUtc(mixed $value, bool $endOfDay = false): ?Carbon
{ {
if (!is_string($value) || trim($value) === '') { if (!is_string($value) || trim($value) === '') {
return null; return null;
} }
try { try {
$value = trim($value); return Carbon::parse(trim($value))->utc();
// Si le front envoie une date simple "2026-01-30"
if ($endOfDay && preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
return Carbon::createFromFormat('Y-m-d', $value, 'UTC')->endOfDay();
}
// ISO 8601 type "2026-01-29T23:00:00.000Z" => UTC garanti
return Carbon::parse($value)->utc();
} catch (\Throwable $e) { } catch (\Throwable $e) {
return null; return null;
} }
} }
private function escapeMeiliString(string $value): string
{
// Échappe les backslashes et les guillemets pour une string Meilisearch
$value = str_replace('\\', '\\\\', $value);
return str_replace('"', '\"', $value);
}
} }