From 3b4c887d3dd4f886bcb5f0167090a8a6590d9f9d Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 30 Jan 2026 16:11:53 +0100 Subject: [PATCH] feat: added logs to hunts controller --- app/Http/Controllers/HuntsController.php | 266 +++++++++++++++++++++-- 1 file changed, 249 insertions(+), 17 deletions(-) diff --git a/app/Http/Controllers/HuntsController.php b/app/Http/Controllers/HuntsController.php index 2f3064f..78a5b5a 100644 --- a/app/Http/Controllers/HuntsController.php +++ b/app/Http/Controllers/HuntsController.php @@ -14,13 +14,35 @@ class HuntsController extends Controller public function index(Request $request): JsonResponse { try { + Log::info('HUNTS_INDEX_START', [ + 'full_request' => $request->all(), + 'url' => $request->fullUrl(), + 'method' => $request->method(), + ]); + $searchTerm = $this->resolveSearchTerm($request); + Log::info('HUNTS_SEARCH_TERM_RESOLVED', [ + 'search_term' => $searchTerm, + ]); + $filters = $this->buildMeilisearchFilters($request); + Log::info('HUNTS_FILTERS_BUILT', [ + 'filters' => $filters, + 'filters_length' => strlen($filters), + ]); + + Log::info('HUNTS_CREATING_SEARCH_QUERY', [ + 'search_term' => $searchTerm ?? '', + 'filters' => $filters, + ]); $search = Hunts::search($searchTerm ?? '') ->query(fn ($query) => $query->with('participants:id,avatar_uri')) ->options(['filter' => $filters]); + Log::info('HUNTS_SEARCH_QUERY_CREATED', [ + 'search_class' => get_class($search), + ]); Log::info('HUNTS_SEARCH', [ 'q' => $searchTerm ?? '', @@ -30,16 +52,93 @@ class HuntsController extends Controller 'request_params' => $request->all(), ]); + Log::info('HUNTS_EXECUTING_PAGINATE'); $result = $search->paginate(10); Log::info('HUNTS_SEARCH_SUCCESS', [ 'total' => $result->total(), 'count' => count($result->items()), + 'result_class' => get_class($result), ]); - return response()->json($result); + // Log the structure of each item + $items = $result->items(); + Log::info('HUNTS_RESULT_ITEMS_COUNT', [ + 'items_count' => count($items), + ]); + + foreach ($items as $index => $item) { + try { + Log::info('HUNTS_ITEM_STRUCTURE', [ + 'index' => $index, + 'item_class' => get_class($item), + 'item_id' => $item->id ?? null, + 'item_title' => $item->title ?? null, + 'has_participants' => isset($item->participants), + 'participants_type' => isset($item->participants) ? gettype($item->participants) : null, + 'participants_class' => isset($item->participants) && is_object($item->participants) ? get_class($item->participants) : null, + ]); + + // Try to access participants + if (isset($item->participants)) { + try { + $participantsData = $item->participants; + Log::info('HUNTS_ITEM_PARTICIPANTS_DATA', [ + 'index' => $index, + 'item_id' => $item->id, + 'participants_count' => is_countable($participantsData) ? count($participantsData) : 'not_countable', + 'participants_value' => $participantsData, + ]); + } catch (\Throwable $participantsError) { + Log::error('HUNTS_ERROR_ACCESSING_PARTICIPANTS', [ + 'index' => $index, + 'item_id' => $item->id, + 'error' => $participantsError->getMessage(), + 'trace' => $participantsError->getTraceAsString(), + ]); + } + } + + // Try to serialize this specific item + try { + $itemJson = json_encode($item); + Log::info('HUNTS_ITEM_JSON_SUCCESS', [ + 'index' => $index, + 'item_id' => $item->id, + 'json_length' => strlen($itemJson), + ]); + } catch (\Throwable $jsonError) { + Log::error('HUNTS_ERROR_JSON_ENCODING_ITEM', [ + 'index' => $index, + 'item_id' => $item->id ?? null, + 'error' => $jsonError->getMessage(), + 'trace' => $jsonError->getTraceAsString(), + ]); + } + } catch (\Throwable $itemError) { + Log::error('HUNTS_ERROR_PROCESSING_ITEM', [ + 'index' => $index, + 'error' => $itemError->getMessage(), + 'trace' => $itemError->getTraceAsString(), + ]); + } + } + + Log::info('HUNTS_CREATING_JSON_RESPONSE'); + $jsonResponse = response()->json($result); + + Log::info('HUNTS_JSON_RESPONSE_CREATED', [ + 'status' => $jsonResponse->status(), + ]); + + return $jsonResponse; } catch (\Throwable $e) { - Log::error('Error fetching hunts: '.$e->getMessage()); + Log::error('HUNTS_ERROR_CAUGHT', [ + 'message' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTraceAsString(), + ]); return response()->json([ 'error' => 'Server Error', @@ -50,79 +149,166 @@ class HuntsController extends Controller private function buildMeilisearchFilters(Request $request): string { + Log::info('BUILD_FILTERS_START', [ + 'has_participating' => $request->has('participating'), + 'has_active' => $request->has('active'), + 'has_city' => $request->filled('city'), + 'has_difficulty' => $request->filled('difficulty'), + ]); + $filters = []; // participating if ($request->has('participating')) { + Log::info('PROCESSING_PARTICIPATING_FILTER', [ + 'participating_raw' => $request->query('participating'), + ]); + $participating = filter_var( $request->query('participating'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ); + Log::info('PARTICIPATING_PARSED', [ + 'participating_value' => $participating, + 'is_bool' => is_bool($participating), + ]); + $userId = auth('sanctum')->id(); // chez toi => UUID string + Log::info('USER_ID_RETRIEVED', [ + 'user_id' => $userId, + 'user_id_type' => gettype($userId), + ]); + if ($userId) { $escapedUserId = $this->escapeMeiliString((string) $userId); + Log::info('USER_ID_ESCAPED', [ + 'escaped_user_id' => $escapedUserId, + ]); + if ($participating === true) { // ✅ UUID => string - $filters[] = "participant_ids = \"{$escapedUserId}\""; + $filter = "participant_ids = \"{$escapedUserId}\""; + $filters[] = $filter; + Log::info('ADDED_PARTICIPATING_TRUE_FILTER', [ + 'filter' => $filter, + ]); } elseif ($participating === false) { - $filters[] = "participant_ids != \"{$escapedUserId}\""; + $filter = "participant_ids != \"{$escapedUserId}\""; + $filters[] = $filter; + Log::info('ADDED_PARTICIPATING_FALSE_FILTER', [ + 'filter' => $filter, + ]); } } elseif ($participating === true) { - $filters[] = "id < 0"; + $filter = "id < 0"; + $filters[] = $filter; + Log::info('ADDED_NO_USER_PARTICIPATING_FILTER', [ + 'filter' => $filter, + ]); } } // active if ($request->has('active')) { + Log::info('PROCESSING_ACTIVE_FILTER', [ + 'active_raw' => $request->query('active'), + ]); + $now = Carbon::now('UTC')->timestamp; $active = filter_var($request->query('active'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + Log::info('ACTIVE_PARSED', [ + 'active_value' => $active, + 'is_bool' => is_bool($active), + 'now_timestamp' => $now, + ]); + if ($active === true) { // Hunt actif = démarré ET (pas terminé OU pas de date de fin) - $filters[] = "start_at_timestamp <= {$now}"; + $filter1 = "start_at_timestamp <= {$now}"; + $filters[] = $filter1; + Log::info('ADDED_ACTIVE_START_FILTER', [ + 'filter' => $filter1, + ]); + // Meilisearch : la syntaxe est 'field EXISTS' et non 'EXISTS field' // Un hunt est actif si end_at_timestamp n'existe pas OU >= now - $filters[] = "(end_at_timestamp >= {$now} OR NOT end_at_timestamp EXISTS)"; + $filter2 = "(end_at_timestamp >= {$now} OR NOT end_at_timestamp EXISTS)"; + $filters[] = $filter2; + Log::info('ADDED_ACTIVE_END_FILTER', [ + 'filter' => $filter2, + ]); } elseif ($active === false) { // Hunt non actif = pas encore commencé OU (déjà terminé ET a une date de fin) // Si pas de end_at_timestamp, le hunt ne peut pas être terminé - $filters[] = "(start_at_timestamp > {$now} OR (end_at_timestamp < {$now} AND end_at_timestamp EXISTS))"; + $filter = "(start_at_timestamp > {$now} OR (end_at_timestamp < {$now} AND end_at_timestamp EXISTS))"; + $filters[] = $filter; + Log::info('ADDED_NOT_ACTIVE_FILTER', [ + 'filter' => $filter, + ]); } } // city if ($request->filled('city')) { + Log::info('PROCESSING_CITY_FILTER', [ + 'city_raw' => $request->query('city'), + ]); + $city = $request->query('city'); if (is_string($city)) { $city = $this->escapeMeiliString($city); - $filters[] = "city = \"{$city}\""; + $filter = "city = \"{$city}\""; + $filters[] = $filter; + Log::info('ADDED_CITY_FILTER', [ + 'filter' => $filter, + ]); } } // difficulty if ($request->filled('difficulty')) { + Log::info('PROCESSING_DIFFICULTY_FILTER', [ + 'difficulty_raw' => $request->query('difficulty'), + ]); + $difficulties = $request->query('difficulty'); if (is_string($difficulties)) { $difficulties = explode(',', $difficulties); } + Log::info('DIFFICULTY_PARSED', [ + 'difficulties' => $difficulties, + 'is_array' => is_array($difficulties), + ]); + if (is_array($difficulties)) { $validDifficulties = array_values(array_intersect( HuntDifficulty::values(), array_map('strtolower', $difficulties) )); + Log::info('VALID_DIFFICULTIES', [ + 'valid_difficulties' => $validDifficulties, + 'count' => count($validDifficulties), + ]); + if (!empty($validDifficulties)) { $difficultyFilters = array_map( - fn ($d) => 'difficulty = "'.$this->escapeMeiliString($d).'"', + fn ($d) => 'difficulty = "' . $this->escapeMeiliString($d) . '"', $validDifficulties ); - $filters[] = '(' . implode(' OR ', $difficultyFilters) . ')'; + $filter = '(' . implode(' OR ', $difficultyFilters) . ')'; + $filters[] = $filter; + Log::info('ADDED_DIFFICULTY_FILTER', [ + 'filter' => $filter, + 'difficulty_filters' => $difficultyFilters, + ]); } } } @@ -133,26 +319,72 @@ class HuntsController extends Controller $endFrom = $this->parseDateFilterUtc($request->query('end_from')); $endTo = $this->parseDateFilterUtc($request->query('end_to')); + Log::info('DATE_FILTERS_PARSED', [ + 'start_from' => $startFrom?->toIso8601String(), + 'start_to' => $startTo?->toIso8601String(), + 'end_from' => $endFrom?->toIso8601String(), + 'end_to' => $endTo?->toIso8601String(), + ]); + if ($startFrom) { - $filters[] = "start_at_timestamp >= {$startFrom->timestamp}"; + // start_at_timestamp est toujours présent (created_at par défaut), donc pas besoin de EXISTS + $filter = "start_at_timestamp >= {$startFrom->timestamp}"; + $filters[] = $filter; + Log::info('ADDED_START_FROM_FILTER', [ + 'filter' => $filter, + 'timestamp' => $startFrom->timestamp, + ]); } if ($startTo) { - $filters[] = "start_at_timestamp <= {$startTo->timestamp}"; + // start_at_timestamp est toujours présent + $filter = "start_at_timestamp <= {$startTo->timestamp}"; + $filters[] = $filter; + Log::info('ADDED_START_TO_FILTER', [ + 'filter' => $filter, + 'timestamp' => $startTo->timestamp, + ]); } if ($endFrom) { - $filters[] = "end_at_timestamp >= {$endFrom->timestamp}"; + // end_at_timestamp peut être null, donc on vérifie qu'il existe avant de comparer + $filter = "(end_at_timestamp EXISTS AND end_at_timestamp >= {$endFrom->timestamp})"; + $filters[] = $filter; + Log::info('ADDED_END_FROM_FILTER', [ + 'filter' => $filter, + 'timestamp' => $endFrom->timestamp, + ]); } if ($endTo) { - $filters[] = "end_at_timestamp <= {$endTo->timestamp}"; + // end_at_timestamp peut être null, donc on vérifie qu'il existe avant de comparer + $filter = "(end_at_timestamp EXISTS AND end_at_timestamp <= {$endTo->timestamp})"; + $filters[] = $filter; + Log::info('ADDED_END_TO_FILTER', [ + 'filter' => $filter, + 'timestamp' => $endTo->timestamp, + ]); } // Filtre par défaut : si aucun filtre n'est spécifié, afficher uniquement les hunts publics // Cela évite l'erreur Meilisearch avec une chaîne de filtres vide if (empty($filters)) { - $filters[] = "is_public = true"; + // En Meilisearch, les booléens peuvent être comparés avec true/false (sans guillemets) + $filter = "is_public = true"; + $filters[] = $filter; + Log::info('ADDED_DEFAULT_PUBLIC_FILTER', [ + 'filter' => $filter, + 'reason' => 'no_filters_provided', + ]); } - return implode(' AND ', $filters); + $finalFilters = implode(' AND ', $filters); + + Log::info('BUILD_FILTERS_COMPLETE', [ + 'filters_array' => $filters, + 'filters_count' => count($filters), + 'final_filters' => $finalFilters, + 'final_filters_length' => strlen($finalFilters), + ]); + + return $finalFilters; } private function resolveSearchTerm(Request $request): ?string