resolveSearchTerm($request); if ($searchTerm !== null) { $hunts = Hunts::search($searchTerm) ->query(fn (Builder $query) => $this->applyHuntFilters( $query->with('participants:id,avatar_uri'), $request )) ->paginate(10); return response()->json($hunts); } $query = $this->applyHuntFilters( Hunts::with('participants:id,avatar_uri'), $request ); $hunts = $query->paginate(10); return response()->json($hunts); } catch (\Throwable $e) { Log::error('Error fetching hunts: '.$e->getMessage()); return response()->json([ 'error' => 'Server Error', 'message' => $e->getMessage(), ], 500); } } private function applyHuntFilters(Builder $query, Request $request): Builder { if ($request->has('participating')) { $participating = filter_var( $request->query('participating'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE ); $userId = auth('sanctum')->id(); Log::info('Participating filter resolved', [ 'participating' => $participating, 'user_id' => $userId, ]); if (! $userId) { if ($participating === true) { $query->whereRaw('1 = 0'); } } else { if ($participating === true) { $query->whereHas('participants', function ($q) use ($userId) { $q->where('user_id', $userId); }); } else { $query->whereDoesntHave('participants', function ($q) use ($userId) { $q->where('user_id', $userId); }); } } } if ($request->has('active')) { $now = Carbon::now(); $active = filter_var($request->query('active'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); if ($active === true) { $query->where('start_at', '<=', $now) ->where(function ($q) use ($now) { $q->whereNull('end_at') ->orWhere('end_at', '>=', $now); }); } else { $query->where(function ($q) use ($now) { $q->whereNull('start_at') ->orWhere('start_at', '>', $now) ->orWhere(function ($q2) use ($now) { $q2->whereNotNull('end_at') ->where('end_at', '<', $now); }); }); } } if ($request->filled('city')) { $city = $request->query('city'); if (is_string($city)) { $query->where('city', 'like', '%'.$city.'%'); } } if ($request->filled('difficulty')) { $difficulties = $request->query('difficulty'); if (is_string($difficulties)) { $difficulties = explode(',', $difficulties); } if (is_array($difficulties)) { $validDifficulties = array_values(array_intersect( HuntDifficulty::values(), array_map('strtolower', $difficulties) )); if (! empty($validDifficulties)) { $query->whereIn('difficulty', $validDifficulties); } } } $startFrom = $this->parseDateFilter($request->query('start_from')); $startTo = $this->parseDateFilter($request->query('start_to')); $endFrom = $this->parseDateFilter($request->query('end_from')); $endTo = $this->parseDateFilter($request->query('end_to')); if ($startFrom) { $query->where('start_at', '>=', $startFrom); } if ($startTo) { $query->where('start_at', '<=', $startTo); } if ($endFrom) { $query->where('end_at', '>=', $endFrom); } if ($endTo) { $query->where('end_at', '<=', $endTo); } return $query; } private function resolveSearchTerm(Request $request): ?string { $searchTerm = $request->query('search') ?? $request->query('q'); if (! is_string($searchTerm)) { return null; } $searchTerm = trim($searchTerm); return $searchTerm !== '' ? $searchTerm : null; } private function parseDateFilter(mixed $value): ?Carbon { if (! is_string($value) || trim($value) === '') { return null; } try { return Carbon::parse($value); } catch (\Throwable $e) { return null; } } /** * Store a newly created resource in storage. */ public function store(StoreHuntRequest $request): JsonResponse { $validated = $request->validated(); $validated['creator_id'] = auth()->id(); $hunt = Hunts::create($validated); return response()->json($hunt, 201); } /** * Display the specified resource. */ /** * Display the specified resource. */ public function show(Request $request, int $id): JsonResponse { $hunt = Hunts::with(['steps', 'participants:id,avatar_uri'])->find($id); if (! $hunt) { return response()->json(['message' => 'Hunt not found'], 404); } $user = $request->user('sanctum'); if ($user) { $participation = $user->participations()->where('hunt_id', $hunt->id)->first(); if ($participation) { // User is a participant $currentStepNumber = $participation->pivot->current_step_number; // Filter steps $hunt->setRelation('steps', $hunt->steps->filter(function ($step) use ($currentStepNumber) { if ($step->step_number < $currentStepNumber) { return true; // Show past steps fully } if ($step->step_number == $currentStepNumber) { // Show hints but hide location for current step $step->makeHidden(['latitude', 'longitude', 'radius_m']); return true; } return false; // Hide future steps })->values()); $hunt->participation_status = $participation->pivot; } elseif ($hunt->creator_id !== $user->id) { // User is logged in but not participant and not creator // Show only first step or nothing specific (keep as is for now, maybe hide all steps or just show basic info) // For now, let's just hide hints of all steps if not participating? // Or maybe just show nothing of steps if not participating? // Requirement: "fetch the hunt in question it retrieves me the hunt steps already passed and the next one" implying context of participation. // If not participating, maybe just return basic hunt info without steps? $hunt->unsetRelation('steps'); } // If creator, show everything (default) } else { // Guest $hunt->unsetRelation('steps'); } return response()->json($hunt); } public function participate(Request $request, int $id): JsonResponse { $hunt = Hunts::findOrFail($id); $user = $request->user(); if ($user->participations()->where('hunt_id', $hunt->id)->exists()) { return response()->json(['message' => 'Already participating'], 409); } $hunt->participants()->attach($user->id, ['current_step_number' => 1, 'status' => 'started']); return response()->json(['message' => 'Participation started'], 201); } public function unparticipate(Request $request, int $id): JsonResponse { $hunt = Hunts::findOrFail($id); $user = $request->user(); if (! $user->participations()->where('hunt_id', $hunt->id)->exists()) { return response()->json(['message' => 'Not participating'], 404); } $hunt->participants()->detach($user->id); return response()->json(['message' => 'Participation stopped'], 200); } /** * Update the specified resource in storage. */ public function update(UpdateHuntRequest $request, int $id): JsonResponse { $hunt = Hunts::find($id); if (! $hunt) { return response()->json(['message' => 'Hunt not found'], 404); } $validated = $request->validated(); $hunt->update($validated); return response()->json($hunt); } /** * Remove the specified resource from storage. */ public function destroy(int $id): JsonResponse { $hunt = Hunts::find($id); if (! $hunt) { return response()->json(['message' => 'Hunt not found'], 404); } $hunt->delete(); return response()->json(['message' => 'Hunt deleted successfully']); } }