feat: added logs to hunts controller

This commit is contained in:
Leon 2026-01-30 16:11:53 +01:00
parent 011f426f96
commit 3b4c887d3d
1 changed files with 249 additions and 17 deletions

View File

@ -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) . '"',
$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