api/app/Http/Controllers/HuntsController.php

175 lines
5.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers;
use App\Enums\HuntDifficulty;
use App\Models\Hunts;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
class HuntsController extends Controller
{
public function index(Request $request): JsonResponse
{
try {
$searchTerm = $this->resolveSearchTerm($request);
$filters = $this->buildMeilisearchFilters($request);
$search = Hunts::search($searchTerm ?? '')
->query(fn ($query) => $query->with('participants:id,avatar_uri'));
// ✅ IMPORTANT: nenvoie PAS filter sil est vide
if ($filters !== '') {
$search->options(['filter' => $filters]);
}
Log::info('HUNTS_SEARCH', [
'q' => $searchTerm ?? '',
'filters' => $filters,
'meili_host' => config('scout.meilisearch.host'),
'index' => (new Hunts)->searchableAs(),
]);
return response()->json($search->paginate(10));
} catch (\Throwable $e) {
Log::error('Error fetching hunts: '.$e->getMessage());
return response()->json([
'error' => 'Server Error',
'message' => $e->getMessage(),
], 500);
}
}
private function buildMeilisearchFilters(Request $request): string
{
$filters = [];
// participating
if ($request->has('participating')) {
$participating = filter_var(
$request->query('participating'),
FILTER_VALIDATE_BOOLEAN,
FILTER_NULL_ON_FAILURE
);
$userId = auth('sanctum')->id(); // chez toi => UUID string
if ($userId) {
$escapedUserId = $this->escapeMeiliString((string) $userId);
if ($participating === true) {
// ✅ UUID => string
$filters[] = "participant_ids = \"{$escapedUserId}\"";
} elseif ($participating === false) {
$filters[] = "participant_ids != \"{$escapedUserId}\"";
}
} elseif ($participating === true) {
$filters[] = "id < 0";
}
}
// active
if ($request->has('active')) {
$now = Carbon::now('UTC')->timestamp;
$active = filter_var($request->query('active'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
if ($active === true) {
$filters[] = "start_at_timestamp <= {$now}";
$filters[] = "(end_at_timestamp >= {$now} OR end_at_timestamp IS NULL)";
} else {
$filters[] = "(start_at_timestamp > {$now} OR end_at_timestamp < {$now})";
}
}
// city
if ($request->filled('city')) {
$city = $request->query('city');
if (is_string($city)) {
$city = $this->escapeMeiliString($city);
$filters[] = "city = \"{$city}\"";
}
}
// difficulty
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)) {
$difficultyFilters = array_map(
fn ($d) => 'difficulty = "'.$this->escapeMeiliString($d).'"',
$validDifficulties
);
$filters[] = '(' . implode(' OR ', $difficultyFilters) . ')';
}
}
}
// dates (UTC)
$startFrom = $this->parseDateFilterUtc($request->query('start_from'));
$startTo = $this->parseDateFilterUtc($request->query('start_to'));
$endFrom = $this->parseDateFilterUtc($request->query('end_from'));
$endTo = $this->parseDateFilterUtc($request->query('end_to'));
if ($startFrom) {
$filters[] = "start_at_timestamp >= {$startFrom->timestamp}";
}
if ($startTo) {
$filters[] = "start_at_timestamp <= {$startTo->timestamp}";
}
if ($endFrom) {
$filters[] = "end_at_timestamp >= {$endFrom->timestamp}";
}
if ($endTo) {
$filters[] = "end_at_timestamp <= {$endTo->timestamp}";
}
return implode(' AND ', $filters);
}
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 parseDateFilterUtc(mixed $value): ?Carbon
{
if (!is_string($value) || trim($value) === '') {
return null;
}
try {
return Carbon::parse(trim($value))->utc();
} catch (\Throwable $e) {
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);
}
}