From 6f4aa9f4e4f9b2269d69b804ed67664b338dc9de Mon Sep 17 00:00:00 2001 From: Leon Morival Date: Wed, 29 Apr 2026 15:55:29 +0200 Subject: [PATCH] feat: return xp --- app/Http/Controllers/HuntsController.php | 18 ++++++++++- app/Http/Resources/HuntResource.php | 22 +------------ app/Models/Hunts.php | 15 +++++++++ tests/Feature/HuntIndexTest.php | 39 ++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 tests/Feature/HuntIndexTest.php diff --git a/app/Http/Controllers/HuntsController.php b/app/Http/Controllers/HuntsController.php index af41ad4..6fcd161 100644 --- a/app/Http/Controllers/HuntsController.php +++ b/app/Http/Controllers/HuntsController.php @@ -9,6 +9,7 @@ use App\Notifications\HuntParticipantJoinedNotification; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Carbon; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; class HuntsController extends Controller @@ -20,10 +21,14 @@ class HuntsController extends Controller $filters = $this->buildMeilisearchFilters($request); $search = Hunts::search($searchTerm ?? '') - ->query(fn ($query) => $query->with('participants:id,avatar_uri')) + ->query(fn ($query) => $query + ->with('participants:id,avatar_uri') + ->withSum('steps', 'xp') + ) ->options(['filter' => $filters]); $result = $search->paginate(10); + $this->appendTotalXpToHunts($result->getCollection()); Log::info('HUNTS_SEARCH_SUCCESS', [ 'total' => $result->total(), @@ -448,6 +453,17 @@ class HuntsController extends Controller return $searchTerm !== '' ? $searchTerm : null; } + /** + * @param Collection $hunts + */ + private function appendTotalXpToHunts(Collection $hunts): void + { + $hunts->each(function (Hunts $hunt): void { + $hunt->setAttribute('xp', $hunt->totalXp()); + $hunt->makeHidden('steps_sum_xp'); + }); + } + private function parseDateFilterUtc(mixed $value): ?Carbon { if (! is_string($value) || trim($value) === '') { diff --git a/app/Http/Resources/HuntResource.php b/app/Http/Resources/HuntResource.php index fe3363b..b9ca0de 100644 --- a/app/Http/Resources/HuntResource.php +++ b/app/Http/Resources/HuntResource.php @@ -17,27 +17,7 @@ class HuntResource extends JsonResource public function toArray(Request $request): array { return array_merge(parent::toArray($request), [ - 'xp' => $this->totalXp(), + 'xp' => $this->resource->totalXp(), ]); } - - private function totalXp(): int - { - if ($this->resource->relationLoaded('steps')) { - return (int) $this->resource->steps->sum('xp'); - } - - $stepsSumXp = $this->resource->getAttribute('steps_sum_xp'); - - if ($stepsSumXp !== null) { - return (int) $stepsSumXp; - } - - return (int) ( - $this->resource->steps_sum_xp - ?? $this->resource->steps?->sum('xp') - ?? $this->resource->steps()->sum('xp') - ?? 0 - ); - } } diff --git a/app/Models/Hunts.php b/app/Models/Hunts.php index d10a6ef..e4d9399 100644 --- a/app/Models/Hunts.php +++ b/app/Models/Hunts.php @@ -64,6 +64,21 @@ class Hunts extends Model ->withTimestamps(); } + public function totalXp(): int + { + if ($this->relationLoaded('steps')) { + return (int) $this->steps->sum('xp'); + } + + $stepsSumXp = $this->getAttribute('steps_sum_xp'); + + if ($stepsSumXp !== null) { + return (int) $stepsSumXp; + } + + return (int) $this->steps()->sum('xp'); + } + /** * Configure Meilisearch index settings */ diff --git a/tests/Feature/HuntIndexTest.php b/tests/Feature/HuntIndexTest.php new file mode 100644 index 0000000..a9de8e4 --- /dev/null +++ b/tests/Feature/HuntIndexTest.php @@ -0,0 +1,39 @@ + 'collection']); + app(EngineManager::class)->forgetEngines(); + + $user = User::factory()->create(); + $hunt = Hunts::withoutSyncingToSearch(fn () => Hunts::factory()->create([ + 'title' => 'Treasure hunt', + ])); + + HuntSteps::factory()->create([ + 'hunt_id' => $hunt->id, + 'xp' => 125, + ]); + + HuntSteps::factory()->create([ + 'hunt_id' => $hunt->id, + 'xp' => 75, + ]); + + $response = $this + ->actingAs($user, 'sanctum') + ->getJson('/api/hunts'); + + $response + ->assertOk() + ->assertJsonPath('data.0.id', $hunt->id) + ->assertJsonPath('data.0.xp', 200) + ->assertJsonMissingPath('data.0.steps_sum_xp'); +});